Data isolation
The contract
Every driver implements IsolationDriver:
| Method | Purpose |
|---|---|
provision | Create the tenant's storage (schema/database/rows) |
destroy | Drop it cleanly (terminates active sessions first) |
reset | Drop and recreate (used by tenant:migrate:fresh) |
connect | Open the runtime Lucid connection |
disconnect | Close it |
connectionName | Synchronous resolver for the active query's connection |
migrate | Run migrations against this tenant's storage |
The four drivers
| Driver | Best for | Notes |
|---|---|---|
schema-pg | Default. Most SaaS workloads. | One PG schema per tenant. Strongest balance of isolation and operational cost. |
database-pg | Enterprise tenants needing OS-level isolation. | One PG database per tenant. Requires CREATEDB. CREATE DATABASE outside transactions. |
rowscope-pg | Lightweight workloads, large tenant counts, central reporting. | Shared schema + tenant_id column. Strict scope by default. |
sqlite-memory | Tests only. | In-process SQLite per tenant. Vanishes on process exit. |
Choosing a driver
- Strict isolation, easy backups, easy per-tenant restore →
schema-pg. Nine out of ten cases. - Compliance-driven separation, cross-database
JOINnot required →database-pg. Higher operational cost: per-tenant pooling, backups, replication. - Hundreds of thousands of tiny tenants, central reporting required, write throughput matters →
rowscope-pg. Watch out for forgotten scope; the strict mode catches most cases. - CI / unit tests →
sqlite-memory. Don't ship to production.
Switching drivers
ts
// config/multitenancy.ts
export default defineConfig({
isolation: {
driver: 'schema-pg', // or 'database-pg' | 'rowscope-pg' | 'sqlite-memory'
templateConnectionName: 'tenant',
},
})If you omit the isolation block entirely, the package defaults to { driver: 'schema-pg' } for v1 compatibility.