GLOBAL WORK • Worldwide Delivery · Customers & Projects worldwide
🌐 100% Remote-First ⚡ 24 / 7 Deployment

How to build a multi-tenant SaaS in 14 days with Supabase and FastAPI

18 August 2026 · Euskolabs

Building a scalable SaaS should not take months. In euskolabs we deliver MVPs SaaS multitenant in less than 14 days using an optimized stack: Support for database and auth, FastAPI for backend, Stripe for payments and Docker for deployment. This is the technical guide to how we do it.

Day 1: Architecture and database design

The multi-tenant key is in the database design. We use Row Level Security (RLS) from PostgreSQL via Supabase to isolate data from each tenant without the need for manual filters in each query.

Table outline

-- Tabla de tenants (organizaciones/clientes)
CREATE TABLE tenants (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  name TEXT NOT NULL,
  plan TEXT DEFAULT 'free',
  stripe_customer_id TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Tabla de usuarios vinculada a tenants
CREATE TABLE users (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  email TEXT UNIQUE NOT NULL,
  role TEXT DEFAULT 'member',
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Tabla de datos del SaaS (ejemplo: proyectos)
CREATE TABLE projects (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
  name TEXT NOT NULL,
  status TEXT DEFAULT 'active',
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Row Level Security: cada usuario solo ve datos de su tenant
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

CREATE POLICY "tenant_isolation" ON projects
  FOR ALL USING (tenant_id = (
    SELECT tenant_id FROM users 
    WHERE id = auth.uid()
  ));

With RLS enabled, any query you run Supabase automatically filters by the authenticated user's tenant. There's no way a tenant can access data from another, not even by mistake.

Days 2-5: Backend with FastAPI

FastAPI automatically generates OpenAPI documentation, valids types with Pydantic and supports native async. The backend structure:

app/
├── main.py          # Entry point + routers
├── deps.py          # Dependencias (auth, db)
├── models.py        # Modelos Pydantic
├── routers/
│   ├── tenants.py   # CRUD tenants
│   ├── projects.py  # CRUD proyectos
│   ├── auth.py      # Login/registro via Supabase
│   └── billing.py   # Webhooks Stripe
└── supabase.py      # Cliente Supabase

Authentication Middleware

from fastapi import Depends, HTTPException
from supabase import create_client

async def get_current_user(token: str = Depends(oauth2_scheme)):
    supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
    user = supabase.auth.get_user(token)
    if not user:
        raise HTTPException(401, "No autorizado")
    return user

Days 6-9: Frontend and Stripe

For the front we use Next.js with SSR for SEO and automatic code splits. Integration with Stripe includes:

@app.post("/webhooks/stripe")
async def stripe_webhook(request: Request):
    event = stripe.Webhook.construct_event(
        payload, sig, ENDPOINT_SECRET
    )
    if event["type"] == "invoice.paid":
        tenant_id = event["data"]["object"]["metadata"]["tenant_id"]
        activate_tenant(tenant_id)
    elif event["type"] == "invoice.payment_failed":
        tenant_id = event["data"]["object"]["metadata"]["tenant_id"]
        suspend_tenant(tenant_id)

Days 10-14: Testing, Docker and deployment

Dockerfile

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

docker-composer.yml

services:
  api:
    build: .
    ports: ["8000:8000"]
    env_file: .env
    restart: always
  caddy:
    image: caddy:2
    ports: ["443:443", "80:80"]
    volumes: ["./Caddyfile:/etc/caddy/Caddyfile"]

Caddy manages SSL automatically with Let's Encrypt. The SaaS is live in production with HTTPS.

Conclusions

With this stack we deliver SaaS multitenant functional in 14 days:

You want to launch your SaaS? Request a diagnosis in euskolabs. com.

← Back to the blog

↑