NestJS + Prisma/TypeORM
Integrate Prisma or TypeORM as the data layer in NestJS 11 with proper DI, lifecycle, and testing patterns.
Search across all documentation pages
Integrate Prisma or TypeORM as the data layer in NestJS 11 with proper DI, lifecycle, and testing patterns.
Quick-reference recipe card - copy-paste ready.
Prisma:
import { Injectable, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
import { PrismaClient } from "@prisma/client";
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() { await this.$connect(); }
async onModuleDestroy() { await this.$disconnect(); }
}
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
findAll() { return this.prisma.user.findMany(); }
}When to reach for this: Any NestJS API that needs a database. Prisma for DX and migrations; TypeORM for decorator-driven entities.
// prisma.module.ts
import { Global, Module } from "@nestjs/common";
import { PrismaService } from "./prisma.service.js";
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
// users.service.ts
import { Injectable } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service.js";
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
async findById(id: string) {
return this.prisma.user.findUnique({ where: { id } });
}
async create(data: { name: string; email: string }) {
return this.prisma.user.create({ data });
}
}
// Test override
const mockPrisma = {
user: {
findUnique: async () => ({ id: "1", name: "Test", email: "t@t.com" }),
create: async (args: { data: { name: string; email: string } }) => ({ id: "1", ...args.data }),
},
};import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./user.entity.js";
import { UsersService } from "./users.service.js";
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User) private repo: Repository<User>,
) {}
findAll() { return this.repo.find(); }
}What this demonstrates:
forFeature for entity-scoped repositories| Factor | Prisma | TypeORM |
|---|---|---|
| Schema definition | schema.prisma file | Decorator entities |
| Migrations | prisma migrate | TypeORM migrations |
| Query style | Generated client API | Repository / QueryBuilder |
| NestJS integration | Manual service wrapper | @nestjs/typeorm module |
| Raw SQL | $queryRaw | QueryBuilder raw |
| Team preference | DX-focused teams | Decorator/Spring-like teams |
async transfer(fromId: string, toId: string, amount: number) {
return this.prisma.$transaction(async (tx) => {
await tx.account.update({ where: { id: fromId }, data: { balance: { decrement: amount } } });
await tx.account.update({ where: { id: toId }, data: { balance: { increment: amount } } });
});
}Keep controllers thin; services call Prisma or repositories. See Repository Pattern.
onModuleDestroy.synchronize: true in production - auto-alters schema dangerously. Fix: use migrations only.include forgotten. Fix: use include/select or DataLoader.| Alternative | Use When | Don't Use When |
|---|---|---|
| Prisma | Type-safe client, easy migrations | Heavy raw SQL, existing TypeORM codebase |
| TypeORM | Decorator entities, NestJS native module | Want Prisma-level DX |
| Drizzle ORM | Lightweight, SQL-first | Need mature NestJS integration |
| Raw pg driver | Maximum control | Standard CRUD API |
Common pattern for singleton PrismaService. Alternative: import PrismaModule in each feature module for explicit deps.
Connection pooling via Prisma Accelerate or PgBouncer. Avoid per-invocation $connect without pooling.
Technically yes, practically avoid it. Pick one ORM per service.
Mock PrismaService with overrideProvider, or use a test database with prisma migrate reset.
Same PrismaService injected into message handlers. One DB connection pool per process.
Separate entities/ or prisma/ from controllers. Services bridge the gap.
Run prisma migrate deploy or TypeORM migrations in deploy pipeline before starting the app.
Prisma supports read replicas via extension. TypeORM supports multiple connections in config.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 19, 2026