NestJS + Prisma/TypeORM
Integra Prisma o TypeORM como la capa de datos en NestJS 11 con patrones adecuados de DI, ciclo de vida y pruebas.
Receta
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
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(); }
}Cuándo usarlo: Cualquier API de NestJS que necesite una base de datos. Prisma para DX y migraciones; TypeORM para entidades impulsadas por decoradores.
Ejemplo funcional
Módulo Prisma
// 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 });
}
}
// Sobreescritura de prueba
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 }),
},
};Módulo TypeORM
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(); }
}Lo que esto demuestra:
- PrismaService con ciclo de vida de conexión/desconexión
- PrismaModule global para inyección en toda la aplicación
forFeaturede TypeORM para repositorios con ámbito de entidad- Cliente Prisma simulado para pruebas unitarias
En detalle
Prisma vs TypeORM en NestJS
| Factor | Prisma | TypeORM |
|---|---|---|
| Definición de esquema | Archivo schema.prisma | Entidades de decorador |
| Migraciones | prisma migrate | Migraciones de TypeORM |
| Estilo de consulta | API de cliente generada | Repositorio / QueryBuilder |
| Integración con NestJS | Envoltorio de servicio manual | Módulo @nestjs/typeorm |
| SQL puro | $queryRaw | QueryBuilder puro |
| Preferencia del equipo | Equipos centrados en DX | Equipos centrados en decoradores/Spring |
Patrón de transacción (Prisma)
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 } } });
});
}Patrón de repositorio
Mantén los controladores ligeros; los servicios llaman a Prisma o a los repositorios. Consulta Patrón de repositorio.
Errores comunes
- PrismaService no desconectado - fugas del pool de conexiones al apagarse. Solución: implementa
onModuleDestroy. - Prisma en el ámbito de la solicitud - sobrecarga innecesaria. Solución: PrismaService singleton, pasa el ID del inquilino como parámetro.
- TypeORM
synchronize: trueen producción - altera el esquema automáticamente de forma peligrosa. Solución: usa solo migraciones. - Consultas N+1 en servicios -
includede Prisma olvidado. Solución: usainclude/selecto DataLoader. - Pruebas contra una base de datos real - pruebas lentas y poco fiables. Solución: simula PrismaService o usa Testcontainers.
- Módulo global para todo - oculta dependencias. Solución: global solo para Prisma/Config; módulos de características explícitos.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Prisma | Cliente con tipo seguro, migraciones fáciles | SQL puro pesado, base de código TypeORM existente |
| TypeORM | Entidades de decorador, módulo nativo de NestJS | Quieres DX a nivel de Prisma |
| Drizzle ORM | Ligero, SQL-first | Necesitas una integración madura con NestJS |
| Controlador pg puro | Máximo control | API CRUD estándar |
Preguntas frecuentes
¿Debería PrismaModule ser global?
Patrón común para PrismaService singleton. Alternativa: importa PrismaModule en cada módulo de características para dependencias explícitas.
¿Cómo manejo Prisma en serverless?
Pool de conexiones a través de Prisma Accelerate o PgBouncer. Evita $connect por invocación sin pooling.
¿Puedo usar Prisma y TypeORM?
Técnicamente sí, prácticamente evítalo. Elige un ORM por servicio.
¿Cómo pruebo servicios con Prisma?
Simula PrismaService con overrideProvider, o usa una base de datos de prueba con prisma migrate reset.
¿Cómo funciona esto con los microservicios de NestJS?
El mismo PrismaService inyectado en los manejadores de mensajes. Un pool de conexiones de base de datos por proceso.
¿Deberían las entidades estar en el mismo módulo que los controladores?
Separa entities/ o prisma/ de los controladores. Los servicios cierran la brecha.
¿Cómo manejo las migraciones de la base de datos en CI?
Ejecuta prisma migrate deploy o las migraciones de TypeORM en la pipeline de despliegue antes de iniciar la aplicación.
¿Qué pasa con las réplicas de lectura?
Prisma soporta réplicas de lectura a través de una extensión. TypeORM soporta múltiples conexiones en la configuración.
Relacionado
- Conceptos básicos de NestJS - estructura del módulo
- Inyección de dependencias - cableado de servicios
- Patrón de repositorio - capa de acceso a datos
- Bases de datos - fundamentos de las bases de datos
- Mejores prácticas de NestJS - lista de verificación de la sección
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS activa), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.