📚 Módulo 02: Backend - Injeção de Dependências e CRUD

Neste módulo, transformaremos o PrismaClient independente que geramos no módulo anterior em um Serviço Injetável Global dentro do ecossistema NestJS. Em seguida, faremos uso da “Nest CLI” para gerar automaticamente toda a estrutura arquitetural limpa (Module, Controller, Service) do nosso domínio de Categorias.


⚙️ 1. O Prisma como um Serviço Injetável (Global)

O NestJS baseia-se pesadamente no conceito de Injeção de Dependências (DI). Não devemos instanciar o new PrismaClient() manualmente em cada arquivo. Criaremos um serviço global que fornece o banco para quem precisar.

Gere o módulo Prisma via CLI:

npx nest g module prisma
npx nest g service prisma

src/prisma/prisma.service.ts

Estenda o PrismaClient e acione a conexão quando o módulo iniciar:

import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
    console.log('🐘 Conectado ao PostgreSQL via Prisma');
  }
}

src/prisma/prisma.module.ts

Configure o módulo como @Global() para que ele fique disponível em toda a API sem precisar ser importado repetidamente:

import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Global()
@Module({
  providers: [PrismaService],
  exports: [PrismaService], // Expõe o serviço para os outros módulos usarem
})
export class PrismaModule {}

🏛️ 2. Arquitetura Limpa (O Diagrama de Fluxo)

A arquitetura do NestJS impõe uma divisão estrita de responsabilidades:

  1. Module: A caixa que empacota tudo.
  2. Controller: O porteiro. Recebe o JSON HTTP, não processa regra de negócio, e envia para o serviço.
  3. Service: O engenheiro. Processa regras e solicita dados ao Prisma.
flowchart LR
    A[Internet / Next.js] -->|HTTP GET /categorias| B(CategoriaController)
    B -->|Método buscarTodos()| C{CategoriaService}
    C -->|this.prisma.categoria.findMany| D[(Banco Postgres)]
    D -->|Array de JSON| C
    C -->|Array de JSON| B
    B -->|HTTP 200 OK| A

📦 3. Criando o CRUD de Categorias

Utilize a automação da CLI para gerar os arquivos já amarrados:

npx nest g resource categoria --no-spec
# Escolha a opção: "REST API" e "Y" para CRUD entry points

O Serviço (categoria.service.ts)

import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateCategoriaDto } from './dto/create-categoria.dto';

@Injectable()
export class CategoriaService {
  constructor(private prisma: PrismaService) {}

  async create(createCategoriaDto: CreateCategoriaDto) {
    return this.prisma.categoria.create({
      data: { nome: createCategoriaDto.nome },
    });
  }

  async findAll() {
    return this.prisma.categoria.findMany({
      orderBy: { nome: 'asc' }
    });
  }

  async findOne(id: number) {
    const categoria = await this.prisma.categoria.findUnique({
      where: { id },
    });
    if (!categoria) throw new NotFoundException('Categoria não encontrada');
    return categoria;
  }
}

O Controlador (categoria.controller.ts)

import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { CategoriaService } from './categoria.service';
import { CreateCategoriaDto } from './dto/create-categoria.dto';

@Controller('categorias')
export class CategoriaController {
  constructor(private readonly categoriaService: CategoriaService) {}

  @Post()
  create(@Body() createCategoriaDto: CreateCategoriaDto) {
    return this.categoriaService.create(createCategoriaDto);
  }

  @Get()
  findAll() {
    return this.categoriaService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.categoriaService.findOne(+id);
  }
}

✅ Pré-Requisitos deste Módulo

Antes de passar para a modelagem do catálogo de produtos e validações por DTO, certifique-se de que:


🤔 Por que fizemos assim?


🔍 Checkpoint

  1. Recurso Gerado: Valide se a pasta src/categoria foi populada com a estrutura lógica de controlador, serviço e classes DTO.
  2. Handshake Confirmado: Inicialize a aplicação localmente e certifique-se de que a mensagem 🐘 Conectado ao PostgreSQL via Prisma é exibida no console de debug do terminal.
  3. Rota de Listagem Operante: Dispare uma consulta do tipo GET na rota http://localhost:3000/categorias e confirme se o banco de dados responde listando as categorias didáticas criadas no seeder.

⚠️ Erros Comuns

Erro Causa Solução
Exceção Nest cannot resolve dependencies of CategoriaService ao subir a API O PrismaService não foi exportado no array do PrismaModule ou o módulo do banco não foi importado no módulo raiz. Certifique-se de incluir PrismaService na propriedade exports de prisma.module.ts e de declarar o PrismaModule na lista de imports de app.module.ts.
O NestJS aceita IDs inválidos e lança erros internos 500 no console A rota /categorias/:id recebeu uma string inválida e o serviço falhou ao tentar forçar a coerção direta sem validação. Garanta o uso do parse numérico na passagem de parâmetros de controladores: use o operador unário +id ou o transformador Number(id) nas assinaturas de rotas.
O banco falha ao consultar registros por estarem nulos, mas não sinaliza de forma amigável ao cliente Falta de tratamento de resultado nulo (null) no fluxo do banco de dados na camada de serviço. Sempre verifique o resultado retornado pelo Prisma. Caso seja nulo, lance explicitamente a exceção new NotFoundException('Categoria não encontrada') para unificar as respostas em JSON.

🏁 Conclusão

O seu primeiro módulo (Categoria) foi concluído e demonstra quão verboso, limpo e direto o NestJS e o Prisma são em harmonia. No Módulo 03 implementaremos o Catálogo de Produtos e integraremos um dos conceitos de segurança mais importantes do NestJS: Data Transfer Objects (DTOs) blindados com Validation Pipes.


Voltar para o Sumário