📚 Módulo 02: Backend - Setup do Projeto, Prisma Migrate e Seeding
Neste módulo, realizaremos a inicialização física da API backend com o NestJS CLI, faremos a configuração do Prisma Client conectando-se a um banco de dados local ou na nuvem (Neon PostgreSQL) e criaremos um roteiro passo a passo para versionar o banco e semeá-lo com dados de testes via scripts automáticos em TypeScript.
🏛️ O Fluxo de Injeção de Dependências (DI)
No NestJS, a comunicação entre as camadas segue um fluxo arquitetural limpo através da Injeção de Dependências, isolando responsabilidades.
flowchart LR
classDef nest fill:#ea2845,stroke:#3b0713,stroke-width:2px,color:#fff;
classDef db fill:#003545,stroke:#001f29,stroke-width:2px,color:#fff;
A[Rota /categorias] --> B(CategoriaController):::nest
B -->|DI| C{CategoriaService}:::nest
C -->|DI| D[PrismaService]:::nest
D --> E[(PostgreSQL)]:::db
🛠️ 1. Inicializando o Projeto Backend com NestJS e Prisma
O primeiro passo é gerar a estrutura base do framework. Os comandos abaixo criam o projeto NestJS e instalam as dependências necessárias para a API REST e para a comunicação com o banco:
# 1. Instalar o NestJS CLI globalmente
npm install -g @nestjs/cli
# 2. Gerar o projeto base (Selecione npm como gerenciador de pacotes)
nest new tecloja-backend
# 3. Entrar na pasta do projeto
cd tecloja-backend
# 4. Instalar o Prisma CLI como dependência de desenvolvimento
npm install prisma --save-dev
# 5. Instalar o Prisma Client (produção) e Bcrypt para senhas seguras
npm install @prisma/client bcrypt
npm install @types/bcrypt --save-dev
# 6. Inicializar a estrutura do Prisma
npx prisma init
O comando npx prisma init gerará uma pasta chamada prisma/ com o arquivo schema.prisma e criará um arquivo .env na raiz do projeto.
🔑 2. Configurando Variáveis de Ambiente e Prisma Schema
Abra o arquivo .env gerado na raiz e configure sua string de conexão com o banco de dados do Neon PostgreSQL (ou Docker local):
# .env
DATABASE_URL="postgresql://tecloja_owner:senha_segura@ep-neon-pool.us-east-2.aws.neon.tech/tecloja_db?sslmode=require"
PORT=3000
JWT_SECRET="sua_chave_secreta_super_forte_para_gerar_tokens_jwt"
Copie as definições físicas do Módulo 01 no arquivo prisma/schema.prisma criado na sua máquina.
🚀 3. Executando as Migrações de Banco de Dados (Prisma Migrate)
Com as variáveis configuradas e o mapeamento concluído, criaremos e executaremos nossa primeira migração física para estruturar as tabelas do banco de dados na nuvem:
# Executa e versiona a migração gerando os códigos SQL físicos equivalentes no banco
npx prisma migrate dev --name inicializacao_banco
O Prisma Migrate irá:
- Comparar as definições do seu
schema.prismacom o estado físico atual do banco. - Gerar um arquivo SQL versionado e armazená-lo dentro da pasta
prisma/migrations/. - Executar o script SQL no banco na nuvem criando as tabelas fisicamente.
- Gerar dinamicamente os tipos TypeScript baseados no banco dentro do seu pacote
node_modules/@prisma/client.
🌾 4. Programando o Script de Carga de Dados (Seeding)
Para carregar dados iniciais de catálogo e usuários administrativos para testes locais e deploys rápidos, criaremos um script de seeding em TypeScript.
Crie um arquivo chamado seed.ts dentro do diretório prisma/:
// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function main() {
console.log('🌱 Iniciando seeding do banco de dados...');
// 1. Limpar banco para evitar duplicidade de registros em novos testes
await prisma.itemPedido.deleteMany();
await prisma.pedido.deleteMany();
await prisma.produto.deleteMany();
await prisma.categoria.deleteMany();
await prisma.usuario.deleteMany();
await prisma.papel.deleteMany();
await prisma.cliente.deleteMany();
// 2. Semear Papéis (Roles)
const papelAdmin = await prisma.papel.create({ data: { nome: 'ADMIN' } });
const papelUser = await prisma.papel.create({ data: { nome: 'USER' } });
console.log('✅ Papéis criados.');
// 3. Semear Usuários Administrativos
const senhaCriptografadaAdmin = await bcrypt.hash('admin123', 10);
const senhaCriptografadaUser = await bcrypt.hash('user123', 10);
await prisma.usuario.create({
data: {
username: 'admin@tecloja.com',
password: senhaCriptografadaAdmin,
papelId: papelAdmin.id,
},
});
await prisma.usuario.create({
data: {
username: 'cliente@tecloja.com',
password: senhaCriptografadaUser,
papelId: papelUser.id,
},
});
console.log('✅ Usuários de teste semeados (senhas: admin123 e user123).');
// 4. Semear Categorias
const catSmartphones = await prisma.categoria.create({ data: { nome: 'Smartphones' } });
const catNotebooks = await prisma.categoria.create({ data: { nome: 'Notebooks' } });
console.log('✅ Categorias de catálogo semeadas.');
// 5. Semear Produtos
await prisma.produto.createMany({
data: [
{
nome: 'iPhone 15 Pro Max',
descricao: 'Smartphone Apple com tela Super Retina XDR e câmera de 48MP.',
preco: 8999.00,
estoque: 15,
categoriaId: catSmartphones.id,
},
{
nome: 'Galaxy S24 Ultra',
descricao: 'Smartphone Samsung com recursos avançados de IA e câmera de 200MP.',
preco: 7999.00,
estoque: 20,
categoriaId: catSmartphones.id,
},
{
nome: 'MacBook Air M3',
descricao: 'Notebook Apple ultrafino com processador M3 de 8 núcleos.',
preco: 11499.00,
estoque: 8,
categoriaId: catNotebooks.id,
},
{
nome: 'Dell XPS 13 Plus',
descricao: 'Notebook Dell Premium com tela InfinityEdge 4K e Intel Core i7.',
preco: 9499.00,
estoque: 5,
categoriaId: catNotebooks.id,
},
],
});
console.log('✅ Catálogo de eletrônicos cadastrado com sucesso.');
// 6. Semear Cliente Padrão para compras
await prisma.cliente.create({
data: {
nome: 'José da Silva',
email: 'jose@gmail.com',
cpf: '123.456.789-00',
},
});
console.log('✅ Cliente de testes criado.');
console.log('🚀 Seeding finalizado com sucesso!');
}
main()
.catch((e) => {
console.error('❌ Erro no seeding:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
⚙️ Habilitando o Seeding no NestJS
Para ensinar o Prisma CLI a interpretar seu script em TypeScript nativamente, abra o arquivo package.json na raiz do projeto e insira a propriedade "prisma" no final das configurações do arquivo JSON:
// package.json (no final do arquivo)
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
Agora, execute o semeador de forma automatizada no terminal:
# Comando nativo do Prisma CLI para carregar o seed.ts
npx prisma db seed
✅ Pré-Requisitos deste Módulo
Antes de passar para a validação de DTOs e controle de transações, certifique-se de que:
- A modelagem declarativa do banco de dados no arquivo
prisma/schema.prismafoi feita no Módulo 01. - Você possui as credenciais de acesso ou a connection string (
DATABASE_URL) de um banco de dados PostgreSQL ativo.
🤔 Por que fizemos assim?
- Por que usar o Prisma Migrate (
npx prisma migrate dev) em vez de criar tabelas manualmente por scripts SQL imperativos? O Prisma Migrate atua como um controle de versão (semelhante ao Git) para o banco de dados. Cada alteração estrutural no arquivo de esquema gera um arquivo SQL físico autoguiado na pastaprisma/migrations. Isso garante que toda a equipe de engenharia e os pipelines de deploy rodem as exatas mesmas evoluções de tabelas, prevenindo discrepâncias entre ambientes local e produção. Ele também recria os tipos TypeScript doPrisma Clientnonode_modulesde forma automática. - Por que criar um script de seeding (
seed.ts) com criptografia e comandosdeleteMany()sequenciais? Para permitir testes eficientes, a aplicação necessita de um banco populado de forma homogênea. O uso dobcrypt.hash()garante que os usuários semeados já possuam senhas protegidas idênticas ao fluxo real de produção. Executar os comandos de deleção (deleteMany()) no início do script limpa o banco para evitar erros de restrição de chave única (@@unique/@unique) ao reexecutar o seed, respeitando a ordem de exclusão de chaves estrangeiras (primeiro apaga as tabelas de itens de pedidos, depois produtos, categorias e clientes). - Por que configurar
"ts-node"nopackage.jsonpara o seeding? Como o NestJS e o Prisma utilizam TypeScript, o script de seeding também é escrito em TS para manter a coesão do projeto e permitir o reaproveitamento das definições de tipagem. A ferramentats-nodepermite rodar arquivos TypeScript de forma direta no ambiente Node.js, sem exigir um passo de compilação intermediário manual que geraria arquivos JavaScript na pasta de builddist/.
🔍 Checkpoint
- Versionamento do Banco: Confirme se a pasta
prisma/migrationsfoi criada contendo uma subpasta versionada com o arquivo SQL de criação de tabelas. - Massa de Dados Semeda: Execute o comando
npx prisma db seedno terminal. Certifique-se de que a mensagem🚀 Seeding finalizado com sucesso!é impressa sem nenhuma falha de restrição ou chave. - Acesso ao Banco: Abra o gerenciador de banco de dados (ex: DBeaver ou painel do Neon) e confirme se as tabelas foram devidamente populadas com os produtos e categorias informados.
⚠️ Erros Comuns
| Erro | Causa | Solução |
|---|---|---|
O comando prisma migrate dev falha ao tentar conectar no banco |
A variável DATABASE_URL no arquivo .env está incorreta, com credenciais inválidas ou o banco de dados está offline. |
Revise detalhadamente a string de conexão no .env. Se estiver usando o Neon, certifique-se de incluir a diretiva sslmode=require no final da URL. |
Erro Cannot find module 'ts-node' ao rodar o comando de seeding |
O executável ts-node não está instalado no projeto local como dependência ou não está disponível globalmente. |
Instale o executável nas dependências de desenvolvimento do projeto executando o comando npm install ts-node --save-dev. |
Erro de restrição de chave estrangeira (Foreign key constraint failed) ao executar o script de seed |
A ordem de deleção (deleteMany()) no início do seed está violando a integridade referencial. |
Certifique-se de apagar primeiro as tabelas filhas (que dependem de IDs de terceiros, como ItemPedido e Pedido) antes de apagar as tabelas pai (Produto, Categoria, Cliente). |
🏁 Conclusão
Com o banco de dados carregado e as migrações criadas com sucesso, nosso próximo passo é proteger a integridade lógica da nossa aplicação NestJS.
No Módulo 03, aprenderemos a realizar a validação de entrada de dados de forma automática em tempo de execução utilizando DTOs e Pipes do class-validator, e trataremos as exceções do banco de dados interceptando falhas HTTP e convertendo-as em JSONs elegantes através dos Filtros de Exceção globais do NestJS.