📚 Módulo 01: Backend - Modelagem de Dados com Prisma ORM

Neste módulo, daremos início à construção da API do nosso e-commerce inicializando o NestJS e configurando a nossa camada de dados declarativa utilizando o Prisma ORM.

O Prisma revolucionou a comunicação com bancos de dados no ecossistema TypeScript ao abolir as classes de entidade prolixas (como as do TypeORM ou Hibernate) em favor de um arquivo descritivo .prisma.


🗄️ 1. O Diagrama de Entidade Relacionamento (ER)

A base da TecLoja exige as seguintes entidades: Categorias de produtos, Catálogo de Produtos, Clientes cadastrados, Pedidos faturados e os Itens detalhados desses pedidos (mantendo o histórico do preço congelado).

erDiagram
    CATEGORIA ||--o{ PRODUTO : possui
    PRODUTO ||--o{ ITEM_PEDIDO : compoe
    PEDIDO ||--|{ ITEM_PEDIDO : contem
    USUARIO ||--o{ PEDIDO : realiza

    CATEGORIA {
        Int id PK
        String nome
    }
    PRODUTO {
        Int id PK
        String nome
        Float preco
        Int estoque
        Int categoriaId FK
    }
    USUARIO {
        Int id PK
        String email
        String senha
        String role
    }
    PEDIDO {
        Int id PK
        Float valorTotal
        String status
        Int usuarioId FK
    }
    ITEM_PEDIDO {
        Int id PK
        Int quantidade
        Float precoUnitario
        Int pedidoId FK
        Int produtoId FK
    }

🛠️ 2. Inicialização do Projeto e Configuração do Prisma

Inicie o repositório backend em seu terminal:

# 1. Cria a base limpa do NestJS
npx @nestjs/cli new tecloja-backend

# 2. Entra no diretório
cd tecloja-backend

# 3. Instala a CLI do Prisma e inicializa as pastas
npm install prisma --save-dev
npx prisma init

Ao rodar o init, um arquivo .env foi gerado. Modifique-o para apontar para o seu banco Neon PostgreSQL (ou Docker local):

DATABASE_URL="postgresql://usuario:senha@ep-wispy-water-1234.us-east-2.aws.neon.tech/neondb?sslmode=require"

📝 3. Modelagem Física (schema.prisma)

Abra o arquivo prisma/schema.prisma e modele as tabelas traduzindo fielmente o diagrama ER acima:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Categoria {
  id       Int       @id @default(autoincrement())
  nome     String    @unique
  produtos Produto[]

  @@map("categorias")
}

model Produto {
  id          Int      @id @default(autoincrement())
  nome        String
  descricao   String?  @db.Text
  preco       Decimal  @db.Decimal(10, 2)
  estoque     Int      @default(0)
  
  categoriaId Int
  categoria   Categoria @relation(fields: [categoriaId], references: [id])
  
  itensPedido ItemPedido[]

  @@map("produtos")
}

model Usuario {
  id       Int      @id @default(autoincrement())
  nome     String
  email    String   @unique
  senha    String
  role     String   @default("USER") // 'USER' ou 'ADMIN'
  
  pedidos  Pedido[]

  @@map("usuarios")
}

model Pedido {
  id          Int      @id @default(autoincrement())
  dataCriacao DateTime @default(now())
  status      String   @default("PAGO")
  valorTotal  Decimal  @db.Decimal(10, 2)
  
  usuarioId   Int
  usuario     Usuario  @relation(fields: [usuarioId], references: [id])
  
  itens       ItemPedido[]

  @@map("pedidos")
}

model ItemPedido {
  id            Int     @id @default(autoincrement())
  quantidade    Int
  precoUnitario Decimal @db.Decimal(10, 2)
  
  pedidoId      Int
  pedido        Pedido  @relation(fields: [pedidoId], references: [id], onDelete: Cascade)
  
  produtoId     Int
  produto       Produto @relation(fields: [produtoId], references: [id], onDelete: Restrict)

  @@map("itens_pedido")
}

Para efetivar a criação das tabelas no PostgreSQL e gerar o Client TypeScript, execute:

npx prisma migrate dev --name init_schema

🌱 4. Alimentação de Dados Didáticos (Seed)

Crie o arquivo prisma/seed.ts e adicione produtos e o usuário Admin padrão:

// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcrypt';

const prisma = new PrismaClient();

async function main() {
  const senhaHash = await bcrypt.hash('admin123', 10);
  
  await prisma.usuario.upsert({
    where: { email: 'admin@tecloja.com' },
    update: {},
    create: {
      nome: 'Administrador Supremo',
      email: 'admin@tecloja.com',
      senha: senhaHash,
      role: 'ADMIN',
    },
  });

  const catSmart = await prisma.categoria.upsert({
    where: { nome: 'Smartphones' },
    update: {},
    create: { nome: 'Smartphones' },
  });

  await prisma.produto.create({
    data: {
      nome: 'iPhone 15 Pro',
      descricao: 'Titânio Aeroespacial',
      preco: 7999.00,
      estoque: 15,
      categoriaId: catSmart.id,
    }
  });

  console.log('🌱 Seed executado com sucesso!');
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Configure o comando de seed no final do package.json:

  "prisma": {
    "seed": "ts-node prisma/seed.ts"
  }

E rode: npx prisma db seed



✅ Pré-Requisitos deste Módulo

Antes de passar para a integração do Prisma com o NestJS e criação do CRUD de categorias, certifique-se de que:


🤔 Por que fizemos assim?


🔍 Checkpoint

  1. Variável de Conexão configurada: Confirme se o arquivo .env foi gerado na raiz do backend e se a variável DATABASE_URL aponta para o banco de dados PostgreSQL correspondente.
  2. Versionamento Criado: Verifique se o comando npx prisma migrate dev criou a pasta prisma/migrations contendo as queries SQL físicas executadas no banco de dados.
  3. Tabelas Populadas: Execute o semeador de dados npx prisma db seed e valide na sua ferramenta cliente de banco (DBeaver/DBeaver) se as tabelas foram devidamente populadas com os dados do catálogo didático.

⚠️ Erros Comuns

Erro Causa Solução
A execução de prisma migrate falha com erros de conexão de rede ou rejeição de autenticação A string de conexão cadastrada no arquivo .env possui erros de senha, host ou falta o parâmetro SSL obrigatório do Neon. Verifique detalhadamente as credenciais no .env e garanta que a opção sslmode=require está presente no final da URL.
O TypeScript reclama que os modelos do Prisma ou as funções de consulta não existem no Prisma Client O arquivo de tipos estáticos no node_modules está desatualizado em relação às modificações feitas no arquivo de esquema. Sempre que realizar modificações físicas em schema.prisma, execute o comando npx prisma generate no terminal para atualizar os tipos locais.
Preços de produtos no carrinho apresentando erros matemáticos e perdas de centavos Uso do tipo genérico Float ou Double para armazenar moedas ou valores decimais financeiros. Mapeie campos monetários no Prisma utilizando a diretiva @db.Decimal(10, 2) para garantir a precisão de casas decimais idêntica às regras fiscais e matemáticas.

🏁 Conclusão

Temos um banco Postgres provisionado na nuvem, tabelas construídas e sementes (Seeds) ativas. No Módulo 02, integraremos o Prisma ao motor do NestJS e criaremos o primeiro módulo CRUD (Categorias) utilizando Arquitetura Limpa.


Voltar para o Sumário