18 agosto 2026 · Euskolabs
Construir un SaaS escalable no debería tomar meses. En euskolabs entregamos MVPs SaaS multi-tenant en menos de 14 días usando un stack optimizado: Supabase para base de datos y auth, FastAPI para el backend, Stripe para pagos y Docker para despliegue. Esta es la guía técnica de cómo lo hacemos.
La clave del multi-tenant está en el diseño de la base de datos. Usamos Row Level Security (RLS) de PostgreSQL via Supabase para aislar los datos de cada tenant sin necesidad de filtros manuales en cada query.
-- 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()
));
Con RLS activado, cualquier query que ejecute Supabase automáticamente filtra por el tenant del usuario autenticado. No hay forma de que un tenant acceda a datos de otro, ni siquiera por error.
FastAPI genera automáticamente documentación OpenAPI, valida tipos con Pydantic y soporta async nativo. La estructura del backend:
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
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
Para el frontend usamos Next.js con SSR para SEO y splits de código automáticos. La integración con Stripe incluye:
@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)
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"]
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 gestiona SSL automáticamente con Let's Encrypt. El SaaS queda live en producción con HTTPS.
Con este stack entregamos SaaS multi-tenant funcionales en 14 días:
¿Quieres lanzar tu SaaS? Solicita un diagnóstico en euskolabs.com.