5

API REST Profissional com Fastify e Prisma

A maioria dos tutoriais de API REST para com o CRUD funcionando. Você tem quatro rotas, um prisma.user.findMany() direto no handler, zero validação, zero tratamento de erro. Funciona no Insomnia. Quebra no primeiro request malformado.

Este post monta uma API REST com Fastify e Prisma que trata os problemas que aparecem quando o código vai para staging: validação de payload, error handling centralizado, separação entre handler e lógica de negócio, e logging estruturado. O resultado é uma base que você consegue estender sem reescrever.

Por que Fastify e não Express

A escolha entre Fastify e Express não é sobre "qual é mais rápido" em benchmark sintético. A diferença prática está em três pontos que afetam o dia a dia de desenvolvimento:

CritérioExpressFastify
Validação nativaNão tem. Precisa de middleware externo (express-validator, celebrate)JSON Schema nativo no core, com suporte a Zod via plugin
Serialização de respostaNão tem. res.json() faz JSON.stringify sem schemaSerialização com schema via fast-json-stringify, que pré-compila o serializer
LoggingNão tem. Você instala morgan ou winston por foraPino integrado no core, com request id automático
Sistema de pluginsMiddleware global com app.use(), sem encapsulamentoEncapsulamento por plugin com escopo isolado
TypeScript DXTipagem via @types/express, frequentemente defasadaTipagem first-class, generics nos handlers para schema tipado

Se a sua API tem menos de cinco rotas e você já conhece Express, não vale a migração. Se você está começando um projeto novo com mais de dez endpoints, Fastify entrega mais infraestrutura pronta e menos dependências externas.

Setup inicial: projeto, Prisma e estrutura de pastas

mkdir api-produtos && cd api-produtos
npm init -y
npm install fastify @fastify/cors @prisma/client zod
npm install -D typescript tsx prisma @types/node
npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext --outDir dist --rootDir src --strict
npx prisma init --datasource-provider postgresql

A estrutura que funciona sem over-engineering para uma API de tamanho médio (10-30 endpoints):

src/
  server.ts
  app.ts
  modules/
    product/
      product.routes.ts
      product.service.ts
      product.schema.ts
  lib/
    prisma.ts
  errors/
    app-error.ts
prisma/
  schema.prisma

Essa organização por módulo (feature) escala melhor que a separação por tipo (controllers/, services/, routes/) porque mantém tudo que pertence a um domínio no mesmo diretório. Se você quer ir além nessa direção, o post sobre Clean Architecture com TypeScript e Node.js detalha a separação em camadas com inversão de dependência.

Schema do Prisma e cliente singleton

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

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

model Product {
  id          String   @id @default(cuid())
  name        String   @db.VarChar(255)
  description String?  @db.Text
  priceInCents Int     // armazenar preço em centavos evita problemas de ponto flutuante
  stock       Int      @default(0)
  active      Boolean  @default(true)
  createdAt   DateTime @default(now()) @map("created_at")
  updatedAt   DateTime @updatedAt @map("updated_at")

  @@map("products")
}

O cliente do Prisma precisa ser singleton. Se você instancia new PrismaClient() em cada arquivo, cada instância abre seu próprio connection pool. Em desenvolvimento com hot reload (tsx watch, nodemon), isso esgota conexões do PostgreSQL em minutos.

// src/lib/prisma.ts
import { PrismaClient } from "@prisma/client";

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined;
};

// Reutiliza a instância entre hot reloads em desenvolvimento.
// Em produção, o processo inicia uma vez e isso é irrelevant

---

Leia o artigo completo em [https://www.vivodecodigo.com.br/backend/api-rest-profissional-fastify-prisma-nodejs](https://www.vivodecodigo.com.br/backend/api-rest-profissional-fastify-prisma-nodejs)
Carregando publicação patrocinada...
1