██╗ █████╗ ███████╗ █████╗ ██████╗ ███╗ ██╗ █████╗
██║ ██╔══██╗██╔════╝██╔══██╗██╔════╝ ████╗ ██║██╔══██╗
██║ ███████║███████╗███████║██║ ███╗██╔██╗ ██║███████║
██║ ██╔══██║╚════██║██╔══██║██║ ██║██║╚██╗██║██╔══██║
███████╗██║ ██║███████║██║ ██║╚██████╔╝██║ ╚████║██║ ██║
╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝
SaaS multi-tenancy for AdonisJS 7. Every tenant lives in its own isolated PostgreSQL schema, with a real package behind it: connection routing, circuit breaking, queues, contextual logging, plans and quotas, scheduled backups with retention, read-replica routing, soft delete, and a satellite suite covering audit logs, webhooks, branding, SSO, feature flags, metrics, and Stripe billing.
📖 Full documentation → · Quickstart · Why Lasagna · Comparison vs stancl · Release notes
I built this because the AdonisJS ecosystem deserved a proper multi
tenancy foundation, and because every SaaS I touched eventually outgrew
the tenant_id column. If you've ever exported one customer's data
with a giant WHERE tenant_id = ? JOIN across forty tables and prayed
nothing leaked, you already know the problem this solves.
If you'd rather see it run than read about it, jump to
examples/api/. It's a real AdonisJS 7 app that
exercises every feature, and one npm run test:e2e brings up the stack
and runs 123 tests against it.
🔒 Audited against real production failure modes — cross-tenant isolation under concurrency, quota atomicity, SSO replay, audit immutability, header-vs-domain hijack, and more. See what we verified →
| Feature | What it gives you |
|---|---|
| Schema isolation | Each tenant gets its own tenant_<uuid> PostgreSQL schema, provisioned and routed automatically. |
| Circuit breaker | Opossum wraps every tenant DB call; OPEN state is restored from Redis on restart so a known-down tenant DB fails fast across deploys. One bad schema can't take down the others. |
| Dependency resilience | Per-dependency fail-open/fail-closed degradation policy via ResilienceService. Emits DependencyDegraded for alerting and returns a typed 503 (DependencyUnavailableException) when fail-closed. |
| Lifecycle hooks + 25 typed events | Declarative before / after hooks wired into commands and jobs. 14 tenant/quota-lifecycle + 10 billing + 1 resilience event. |
| Contextual logging | tenantId rides along through HTTP and queue jobs via AsyncLocalStorage. |
tenant:doctor |
Ten built-in checks, --fix for auto-recovery, --json for CI, --watch for a live TUI. |
| Plans and quotas | Declarative plans, rolling counters, snapshot usage, an enforceQuota() middleware that returns 429 and emits TenantQuotaExceeded. |
| Scheduled backups + retention | Tier-based intervals and keepLast, S3 mirror with purge awareness, idempotent cron command. |
| Health probes + Prometheus | /livez, /readyz, /healthz, /metrics. No prom-client peer dep. |
| Read replica routing | Round-robin, random, or sticky-by-tenant-id with stable connection naming. |
| REST admin API | 36 endpoints + OpenAPI 3.1 spec + Swagger UI. You bring the auth middleware. |
| Soft delete TTL | Recycle bin pattern. --keep-schema on destroy, tenant:purge-expired on a cron. |
| Nine satellites | Audit logs (append-only at the SQL level), webhooks (HMAC-signed + retries + verifier helper), quotas, feature flags, branding, SSO/OIDC, metrics, impersonation, and Stripe billing (idempotent webhook + dunning + metered + checkout/portal + lifecycle). All optional. |
Two questions to ask before adopting:
- Do you actually need true tenant isolation, or is a
tenant_idcolumn enough? If you want both at-rest separation and per-tenant migrations, this is for you. If you don't, save yourself the operational complexity. - Are you on PostgreSQL? Schemas are a Postgres-native concept. MySQL and MariaDB users should look elsewhere.
npm install @adonisjs-lasagna/saas-tenancy
node ace configure @adonisjs-lasagna/saas-tenancyThe configure command registers the provider, publishes
config/multitenancy.ts, scaffolds app/models/backoffice/tenant.ts,
and (selectively) publishes satellite migration stubs.
# Selective satellites
node ace configure @adonisjs-lasagna/saas-tenancy --with=audit,webhooks
# CI (no prompt)
node ace configure @adonisjs-lasagna/saas-tenancy --no-interaction --with=audit,brandingThe full 5-minute quickstart covers DB connections, the tenant repository binding, middleware registration, and creating your first tenant.
The complete documentation lives at arcoders.github.io/Adonisjs-lasagna-saas-tenancy. Direct links:
- 🚀 Quickstart
- 🚢 Deployment guide (Dockerfile, docker-compose, Helm chart)
- 🛡️ Security guide (what the package guarantees vs what the host owns)
- ⚖️ Comparison vs
stancl/tenancy
examples/api/ is a real AdonisJS 7 app that wires
every feature end-to-end:
cd examples/api
npm install
docker compose -f compose.test.yml up -d
npm run test:e2eThe 123-test e2e suite covers provisioning, schema isolation, contextual logging across HTTP + queue, the doctor command, backups + restore + clone, quotas → 429, lifecycle events, the admin REST API, mail context propagation, replica routing, and the webhook delivery state machine.
- Node.js 24+, ESM-native (
module: NodeNext) - AdonisJS 7 with
@adonisjs/lucid,@adonisjs/queue,@adonisjs/redis - PostgreSQL 14+ (the package does not target MySQL/MariaDB)
- Optional peers:
@adonisjs/drive,@adonisjs/mail,@adonisjs/session,@aws-sdk/client-s3,jose(only when SSO is used),better-sqlite3(in-memory testing driver)
npm install --legacy-peer-deps
npm run typecheck
npm test
docker compose -f compose.test.yml up -d
npm run test:integration
npm run docs:dev # live preview of the docs sitePRs welcome. Please add tests for any behavior change and run the
typecheck + unit suite before pushing. CI gates: typecheck, unit
tests, integration tests against real Postgres + Redis, e2e demo app
suite, knip (informational), and npm audit --audit-level=high.
MIT — see LICENSE.