This is a starter kit for creating multi-tenant NestJS APIs using Prisma and PostgreSQL.
MAIN_DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
TENANT_DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
Update the database URLs as required. If the database instances do not contain the right schema, you can create that using prisma db push
once the dependencies are ready.
pnpm install
Once the installation completes, postinstall
should generate the PrismaClients.
pnpm dev
pnpm start
There are two .prisma
files inside ./prisma/
.
schema.prisma
for the Main Databasetenant-schema.prisma
for the Tenant Database
Do the necessary changes in the appropriate file and run the correct script.
pnpm prisma:main:push
pnpm prisma:tenant:push
import { MainPrismaService } from '@/modules/prisma/main-prisma.service';
export class NestJSComponent {
constructor(
private readonly mainPrisma: MainPrismaService,
) {}
}
> Example:
@Get('/tenants')
async getTenants() {
const tenants = await this.mainPrisma.tenant.findMany();
return { tenants };
}
import { TENANT_PRISMA_SERVICE, TenantPrismaService } from '@/modules/prisma/tenant-prisma.service';
export class NestJSComponent {
constructor(
@Inject(TENANT_PRISMA_SERVICE) private readonly tenantPrisma: TenantPrismaService
) {}
}
> Example:
@Get('/users')
async getUsers() {
/**
* Since we're using query extensions with the Prisma client,
* this query should return only the users with the column
* "tenantId" matching that in the request "x-tenant-code".
*/
const users = await this.tenantPrisma.user.findMany();
return { users };
}
flowchart TB
Client((Client))
API((API))
MainDB[(MainDB)]
Guards[[AuthGuard<br>UserGuard<br>RolesGuard]]
Client --Request--> API --> RequestLoggerMiddleware --> TenantDatasourceMiddleware --> Guards --> Controller --> API --Response--> Client
TenantDatasourceMiddleware---MainDB
erDiagram
Main--Tenant {
int id PK
string code
string name
string website
json metadata
int datasourceId FK
}
Main--Datasource {
int id PK
string name
string url
json metadata
}
TenantDBX--Entity {
int id PK
any key
int tenantId FK
}
Main--Tenant ||--o| Main--Datasource : has
Main--Tenant ||--o{ TenantDBX--Entity : has
Datasource | Main DB | Tenant DB 1 | Tenant DB 2 | Tenant DB 3 |
---|---|---|---|---|
Tenant(s) | Metadata | A B C |
D E F |
G H I |
flowchart LR
API((API Main))
APITSM[[TenantDatasourceMiddleware]]
APIController[[Controller]]
MainDb[(Main DB)]
TenantDb1[(Tenant DB 1)]
TenantDb2[(Tenant DB 2)]
TenantDb3[(Tenant DB 3)]
ClientA[Client A]
ClientB[Client B]
ClientC[Client C]
ClientD[Client D]
ClientE[Client E]
ClientF[Client F]
ClientG[Client G]
ClientH[Client I]
API <--(1)--> APITSM <--Get datasourceUrl--> MainDb
API --(2)--> APIController
ClientA --Data A--> API
ClientB --Data B--> API
ClientC --Data C--> API
ClientD --Data D--> API
ClientE --Data E--> API
ClientF --Data F--> API
ClientG --Data G--> API
ClientH --Data H--> API
ClientI --Data I--> API
APIController -.Data A.->TenantDb1
APIController -.Data B.->TenantDb1
APIController -.Data C.->TenantDb1
APIController -.Data D.->TenantDb2
APIController -.Data E.->TenantDb2
APIController -.Data F.->TenantDb2
APIController -.Data G.->TenantDb3
APIController -.Data H.->TenantDb3
APIController -.Data I.->TenantDb3