NestJS + Prisma/TypeORM
Integre Prisma ou TypeORM como a camada de dados no NestJS 11 com DI, ciclo de vida e padrões de teste adequados.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
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(); }
}Quando usar isso: Qualquer API NestJS que precise de um banco de dados. Prisma para DX e migrações; TypeORM para entidades orientadas a decoradores.
Exemplo de Trabalho
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 });
}
}
// 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 }),
},
};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(); }
}O que isso demonstra:
- PrismaService com ciclo de vida de conexão/desconexão
- Global PrismaModule para injeção em todo o aplicativo
- TypeORM
forFeaturepara repositórios com escopo de entidade - Cliente Prisma mockado para testes unitários
Análise Profunda
Prisma vs TypeORM no NestJS
| Fator | Prisma | TypeORM |
|---|---|---|
| Definição de esquema | Arquivo schema.prisma | Entidades decoradoras |
| Migrações | prisma migrate | Migrações TypeORM |
| Estilo de consulta | API de cliente gerada | Repositório / QueryBuilder |
| Integração NestJS | Wrapper de serviço manual | Módulo @nestjs/typeorm |
| SQL Bruto | $queryRaw | QueryBuilder raw |
| Preferência da equipe | Equipes focadas em DX | Equipes orientadas a decoradores/estilo Spring |
Padrão de Transação (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 } } });
});
}Padrão de Repositório
Mantenha os controladores enxutos; os serviços chamam Prisma ou repositórios. Veja Padrão de Repositório.
Armadilhas
- PrismaService não desconectado - vazamento do pool de conexões no desligamento. Correção: implementar
onModuleDestroy. - Prisma em escopo de requisição - sobrecarga desnecessária. Correção: PrismaService singleton, passe o ID do locatário como parâmetro.
- TypeORM
synchronize: trueem produção - altera automaticamente o esquema perigosamente. Correção: use apenas migrações. - Consultas N+1 em serviços -
includedo Prisma esquecido. Correção: useinclude/selectou DataLoader. - Testando contra banco de dados real - testes lentos e instáveis. Correção: mockar PrismaService ou usar Testcontainers.
- Módulo global para tudo - oculta dependências. Correção: global apenas para Prisma/Config; módulos de recursos explícitos.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Prisma | Cliente type-safe, migrações fáceis | SQL bruto pesado, base de código TypeORM existente |
| TypeORM | Entidades decoradoras, módulo nativo NestJS | Deseja DX nível Prisma |
| Drizzle ORM | Leve, SQL-first | Precisa de integração NestJS madura |
| Driver pg bruto | Controle máximo | API CRUD padrão |
FAQs
O PrismaModule deve ser global?
Padrão comum para PrismaService singleton. Alternativa: importar PrismaModule em cada módulo de recurso para dependências explícitas.
Como lidar com Prisma em serverless?
Pooling de conexão via Prisma Accelerate ou PgBouncer. Evite $connect por invocação sem pooling.
Posso usar Prisma e TypeORM juntos?
Tecnicamente sim, na prática evite. Escolha um ORM por serviço.
Como testar serviços com Prisma?
Mockar PrismaService com overrideProvider, ou usar um banco de dados de teste com prisma migrate reset.
Como isso funciona com microsserviços NestJS?
O mesmo PrismaService injetado nos manipuladores de mensagens. Um pool de conexões de banco de dados por processo.
As entidades devem estar no mesmo módulo que os controladores?
Separe entities/ ou prisma/ dos controladores. Serviços fazem a ponte.
Como lidar com migrações de banco de dados em CI?
Execute prisma migrate deploy ou migrações TypeORM no pipeline de implantação antes de iniciar o aplicativo.
E as réplicas de leitura?
Prisma suporta réplicas de leitura via extensão. TypeORM suporta múltiplas conexões na configuração.
Relacionados
- Noções Básicas de NestJS - estrutura de módulos
- Injeção de Dependência - conexão de serviços
- Padrão de Repositório - camada de acesso a dados
- Bancos de Dados - fundamentos de banco de dados
- Melhores Práticas do NestJS - lista de verificação da seção
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, e NestJS 11.