A battle-tested, highly configurable multi-tenant backend template built on NestJS and Fastify, utilizing PostgreSQL schemas and Drizzle ORM.
This template is designed to be completely configuration-driven and plug-and-play. Simply clone the repo, fill out your .env secrets, define your JSON policies, and launch a production-ready enterprise SaaS backend instantly.
Used across email templates (src/mail/templates/layout.hbs), the favicon (public/favicon.png), and the logo (public/assets/logo.png).
| Name | Hex | Swatch |
|---|---|---|
| Darkest | #001E5F |
![]() |
| Dark | #002982 |
![]() |
| Active | #00379E |
![]() |
| Primary | #0041BA |
![]() |
| Secondary | #CFD8E8 |
![]() |
| Accent | #D38A00 |
![]() |
I chose Fastify over Express and Drizzle ORM over Prisma/TypeORM because when building a scalable multi-tenant SaaS, performance is non-negotiable.
xychart-beta
title "Requests per Second (Throughput) 🔥"
x-axis ["Express + TypeORM", "Express + Prisma", "Fastify + Drizzle ORM"]
y-axis "Req/Sec" 0 --> 70000
bar [12000, 18000, 65000]
Fastify handles significantly more requests per second than Express, and Drizzle's zero-overhead SQL generation means no heavy runtime engine dragging down your DB queries.
This engine guarantees absolute data isolation by separating tenant data at the PostgreSQL schema level.
graph TD
Client[Client App / Frontend] -->|API Request| Gateway[Fastify / NestJS API]
Gateway --> Context{TenantContextService}
Context -->|Root Request| RootDB[(Root Schema)]
Context -->|Tenant Request| TenantDB[(Tenant Schema: tenant_xyz)]
subgraph PostgreSQL Database
RootDB -.-> |system_settings| Global[Global Configurations]
RootDB -.-> |accounts| Tenants[Tenant Registry]
RootDB -.-> |billing| Subscriptions[Global Subscriptions]
TenantDB -.-> |users| TUsers[Tenant Employees]
TenantDB -.-> |roles| TRoles[Tenant Custom Roles]
TenantDB -.-> |locations| TLocs[Tenant Locations]
end
- Root Schema (
root): Stores platform-level data (Accounts, Root Users, System Settings, Global Subscriptions). - Tenant Schemas (
tenant_<accountId>): Stores strictly isolated operational data per company.
Tenant context is dynamically propagated through the entire request lifecycle using AsyncLocalStorage via the TenantContextService.
Caution
Strict Mode Enforcement I've removed hardcoded fallbacks from the codebase. The engine is strict: if a required environment variable or policy is missing, it fails fast on boot. This guarantees your production environment is exactly what you expect.
Copy .env.example to .env and fill in your secrets. Joi actively validates these at boot (src/config/validation.schema.ts) — a missing Required variable fails the app fast at startup instead of surfacing as a runtime bug later.
cp .env.example .env| Variable | Required | Default | Description |
|---|---|---|---|
NODE_ENV |
No | development |
development | production | test |
PORT |
Yes | — | Port the Fastify server listens on |
API_BASE_URL |
Yes | — | Public base URL of this API — used to build OAuth callback URLs and asset links (logo, email header) served from /public |
FRONTEND_URL |
Yes | — | Base URL of the frontend app — used in email links (dashboard, password reset) |
DATABASE_URL |
Yes | — | Postgres connection string |
MIGRATIONS_AUTO_APPLY |
No | false |
Run Drizzle migrations automatically on boot |
DB_POOL_MAX |
Yes | — | Max connections for the template (public) schema pool |
TENANT_POOL_MAX |
Yes | — | Max connections per per-tenant schema pool |
TENANT_POOL_IDLE_TTL_MS |
Yes | — | Idle time (ms) before an unused tenant pool is evicted |
REDIS_HOST |
Yes | — | Redis host (queues) |
REDIS_PORT |
Yes | — | Redis port |
JWT_ACCESS_SECRET |
Yes | — | Signing secret for access tokens |
JWT_REFRESH_SECRET |
Yes | — | Signing secret for refresh tokens |
JWT_ACCESS_EXPIRES_IN |
Yes | — | Access token lifetime (e.g. 15m) |
JWT_REFRESH_EXPIRES_IN |
Yes | — | Refresh token lifetime (e.g. 30d) |
SMTP_HOST |
Yes | — | SMTP host for transactional email |
SMTP_PORT |
Yes | — | SMTP port (465 = implicit TLS) |
SMTP_USER |
No | (empty) | SMTP auth username |
SMTP_PASS |
No | (empty) | SMTP auth password |
MAIL_FROM |
Yes | — | From address on all outgoing email |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
No | — | Google OAuth SSO |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET |
No | — | GitHub OAuth SSO |
MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET |
No | — | Microsoft (Azure AD) OAuth SSO |
DISCORD_CLIENT_ID / DISCORD_CLIENT_SECRET |
No | — | Discord OAuth SSO |
APPLE_CLIENT_ID / APPLE_TEAM_ID / APPLE_KEY_ID / APPLE_PRIVATE_KEY |
No | — | Sign in with Apple |
SSO variables are optional as a group — only fill in the providers you actually enable; unset ones simply leave that strategy unconfigured.
I use a robust, module-based permission architecture (e.g., view_billing, manage_users) rather than flat generic permissions. You control the entire RBAC system natively via two JSON files at the root:
rbac-root.json: Defines roles (e.g.,Super Admin) and module-specific permissions for system administrators managing the platform.rbac-tenant.json: Defines the default blueprint of roles (e.g.,Admin,Mobile) and module-specific permissions for users operating inside a tenant account.
At the root of the project, you'll find auth-policy.json. This securely controls our dynamic, sliding-window refresh token architecture.
sequenceDiagram
participant User
participant Engine
participant AuthPolicy
User->>Engine: Refresh Token Request (Device #3)
Engine->>AuthPolicy: Check Max Sessions (e.g., limit 2)
AuthPolicy-->>Engine: Limit Exceeded!
Engine->>Engine: Evict Oldest Active Session (Device #1)
Engine-->>User: Issue New Access & Refresh Tokens
You can dynamically configure the maximum number of concurrent device sessions allowed for your users (tenantWeb, tenantMobile, root).
TheBlueprintCode is built to be a robust, open-source foundation, and I am actively looking for community contributions!
While the core architecture is complete, integrating with paid, enterprise-level third-party services requires specialized accounts that I don't always have access to.
Here are the primary areas where I'd love your help:
The authentication system natively supports JWT-based login and free SSO strategies (✅ Google, ✅ GitHub, ✅ Microsoft, ✅ Discord, ✅ Apple). However, I need help implementing Passport.js strategies for:
- Okta
- Auth0
- SAML 2.0
- Facebook / Meta
The engine includes a strict BillingService interface and database tables for subscriptions. I need help building the adapters for:
- Stripe
- Zoho Billing
- Razorpay
The engine currently falls back to a functional LocalStorageAdapter. I need help building out cloud integrations like:
- AWS S3
- Google Cloud Storage
- Fork the repository.
- Add your integration (e.g., in
src/common/billing/orsrc/auth/strategies/). - Read secrets strictly using
this.configService.getOrThrow(). - Open a Pull Request!
# Install dependencies
npm install
# Start local infrastructure (Postgres & Redis)
docker-compose up -d
# One-shot bootstrap: creates .env (with generated JWT secrets), runs
# migrations, and interactively seeds a root admin + a first tenant company
npm run setup
# Start the NestJS backend
npm run start:devnpm run setup is idempotent — re-running it skips .env creation if it already exists, and skips seeding any root user / tenant account that's already there.






