Blog
Software2024-09-1511 min

Multi-tenancy real en Next.js + Prisma: lo que aprendimos con OmniTrip

Construir un SaaS multi-tenant con Next.js 14 App Router y Prisma tiene decisiones de arquitectura que impactan todo el desarrollo posterior.

Qué significa multi-tenant en la práctica

Multi-tenancy significa que múltiples operadoras de turismo aventura usan la misma aplicación, cada una con sus propios datos, usuarios y configuraciones, sin que los datos de una se mezclen con los de otra.

Hay tres modelos principales: database per tenant, schema per tenant, y shared database con columna tenant_id. Elegimos el tercero, que es el que tiene más trampas.

El problema del tenant_id olvidado

Con el modelo shared database, el riesgo principal es olvidar filtrar por tenant_id en alguna query. En una aplicación con docenas de modelos y cientos de queries, esto pasa. Y cuando pasa en producción, una operadora ve los datos de otra.

La solución fue un helper de Prisma que envuelve el cliente y agrega automáticamente el filtro de tenant en todas las operaciones.

Row Level Security como segunda capa de defensa

Además del filtrado a nivel de aplicación, implementamos Row Level Security (RLS) en PostgreSQL. RLS define políticas directamente en la base de datos: aunque un bug en el código omita el filtro de tenant_id, la base de datos rechaza el acceso a filas de otros tenants.

Coolify como plataforma de deploy para SaaS

OmniTrip corre en Coolify sobre un VPS de Hetzner. El workflow de deploy: push a main dispara un webhook, Coolify hace pull, ejecuta docker build, corre prisma migrate deploy como parte del entrypoint, y hace un rolling restart del contenedor.

Lo que cambiía hoy

Hoy usaría Prisma Client Extensions para el aislamiento de tenant en lugar del helper manual. Las extensiones permiten extender el cliente con lógica custom de forma más tipada y menos propensa a errores.