- Artigos
- Desenvolvimento
- Node.js e Fastify: APIs rápidas e tipadas sem excesso de complexidade

Node.js e Fastify: APIs rápidas e tipadas sem excesso de complexidade
Quando você precisa de uma API em Node.js que responda rápido, valide entradas com confiança e continue agradável de manter conforme o projeto cresce, o Fastify merece atenção de verdade. Ele não é um framework full-stack como o NestJS nem um roteador mínimo como o Express. Fica num meio-termo prático: opinativo sobre desempenho e ciclo de vida das requisições, mas flexível o suficiente para monolitos, microsserviços e deploys serverless.
Este artigo mostra por que o Fastify combina com backends modernos, como os conceitos centrais aparecem em projetos reais e quais padrões mantêm suas APIs rápidas e sustentáveis em produção.
Por que Fastify no Node.js
O Node.js brilha em cargas limitadas por I/O: consultas a banco, chamadas HTTP, leitura de arquivos e consumidores de filas. O event loop lida com concorrência sem abrir uma thread por requisição, o que mantém o uso de memória previsível sob carga. O que o Node.js não entrega pronto é uma forma estruturada de validar payloads, serializar respostas ou compor middleware com segurança entre equipes.
O Fastify preenche essa lacuna com escolhas de design deliberadas:
- Roteamento enxuto — O roteador e o pipeline de serialização do Fastify foram pensados para throughput. Em muitos benchmarks ele supera o Express e continua competitivo com alternativas mais leves, especialmente quando a serialização JSON é frequente.
- JSON Schema como contrato de primeira classe — Rotas declaram schemas para body, querystring, params e formatos de resposta. O Fastify compila validadores na subida, então o custo por requisição permanece baixo.
- Encapsulamento via plugins — Plugins criam escopos isolados para rotas, decorators e hooks. Isso evita poluição global e facilita dividir aplicações grandes por domínio.
- Hooks de ciclo de vida previsíveis — onRequest, preHandler, preSerialization e onResponse oferecem pontos de extensão claros, sem cadeias de middleware inventadas caso a caso.
Se você já conhece Express, a mudança de mentalidade é pequena: ainda registra rotas e handlers. A diferença é que o Fastify empurra você cedo para contratos explícitos e plugins composáveis — e isso compensa quando várias pessoas mexem no mesmo serviço.
Estrutura de projeto que escala
Um erro comum é tratar o Fastify como script de arquivo único. Para qualquer coisa além de protótipo, organize por feature ou contexto delimitado, não só por camada técnica.
Uma estrutura que funciona bem em projetos TypeScript:
- src/app.ts — Cria a instância Fastify, registra plugins globais (CORS, sensible, autoload) e exporta uma função de build para testes.
- src/plugins/ — Preocupações transversais: autenticação, conexão com banco, contexto de log, rate limiting.
- src/modules/users/ — Rotas, handlers e schemas de um domínio.
- src/modules/orders/ — Mesmo padrão, isolado de users via encapsulamento de plugin.
Com o @fastify/autoload, dá para carregar plugins e rotas de diretórios automaticamente. Reduz boilerplate e mantém fronteiras explícitas. Cada módulo exporta um plugin Fastify que registra suas rotas e hooks, então importar um módulo em app.ts vira uma linha.
Em TypeScript, combine o Fastify com fastify-type-provider-zod ou tipos gerados a partir dos schemas. Corpos de requisição e respostas tipados pegam bugs de integração em tempo de compilação, não nos logs de produção.
Schemas: validação, documentação e serialização
O suporte a schemas é o recurso que muitas equipes ignoram no começo e passam a depender depois. Ao anexar um schema à rota, três coisas acontecem: dados de entrada são validados, saída pode ser serializada por um serializer compilado, e você ganha base para documentação OpenAPI.
Defina schemas perto da rota, mas reutilize fragmentos compartilhados para tipos comuns como paginação, params UUID ou envelopes de erro:
const listUsersSchema = {
querystring: {
type: 'object',
properties: {
page: { type: 'integer', minimum: 1, default: 1 },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 20 }
}
},
response: {
200: {
type: 'object',
properties: {
data: { type: 'array', items: { $ref: 'User#' } },
meta: { $ref: 'PaginationMeta#' }
}
}
}
};Registre definições compartilhadas uma vez com addSchema e referencie com $ref. Isso estabiliza a saída OpenAPI e evita copiar listas de propriedades entre endpoints.
Falhas de validação retornam 400 estruturados automaticamente. Combine com um plugin global de tratamento de erros para normalizar erros de banco, falhas de auth e exceções de domínio num JSON consistente em que os clientes possam confiar.
Plugins, decorators e injeção de dependência
O modelo de plugins do Fastify não é cosmético. Plugins podem ser síncronos ou assíncronos, e o Fastify aguarda o registro assíncrono antes de montar rotas dependentes. A ordem de subida fica explícita: conectar ao Postgres, registrar repositórios, montar rotas HTTP.
Decorators anexam serviços compartilhados à instância Fastify ou ao objeto de requisição. Um padrão típico:
- fastify.decorate('db', pool) — Pool de banco compartilhado na instância da app.
- fastify.decorateRequest('user', null) — Contexto de usuário por requisição, definido num hook de auth.
Como decorators vivem dentro do escopo de um plugin, dois módulos podem usar clientes de banco ou estratégias de auth diferentes sem colisão de nomes. Esse encapsulamento é mais difícil de garantir em frameworks que dependem de singletons globais.
Em testes, chame sua factory buildApp() com plugins substituídos: troque o banco real por stub em memória, simule clientes HTTP externos e injete fixtures pelos mesmos decorators do código de produção. Testes de integração contra uma instância Fastify real pegam incompatibilidades de roteamento e schema que testes unitários isolados de handlers não detectam.
Hábitos de performance que importam de verdade
O Fastify é rápido por padrão, mas o código da aplicação ainda domina a latência. Foque nestas alavancas antes de micro-otimizar o framework:
- Evite bloquear o event loop — Trabalho pesado de CPU (PDF grande, processamento de imagem, agregações complexas) vai para worker threads, fila ou serviço separado.
- Use pool de conexões — Reutilize conexões de banco e Redis via plugins registrados na subida. Abrir conexão nova por requisição destrói throughput.
- Faça stream de respostas grandes — Para exportações e downloads, use streams em vez de bufferizar payloads inteiros na memória.
- Mantenha serializers estritos — Schemas de resposta removem propriedades desconhecidas e reduzem payload. Clientes recebem JSON previsível e você evita vazar campos internos.
- Ative logging com critério — O logger nativo (Pino) é eficiente, mas logar body completo em produção gera ruído e custo. Prefira IDs de correlação e metadados estruturados.
Para cache, @fastify/caching ou um plugin Redis na camada preHandler funciona bem em endpoints de leitura intensa. Invalide chaves nos handlers de escrita em vez de confiar só em TTL para dados que exigem consistência forte.
Segurança e operação em produção
O Fastify não protege sua API magicamente, mas integra bem com práticas padrão:
- Registre @fastify/helmet para headers HTTP sensatos.
- Valide e limite tamanho de payload com bodyLimit e schemas estritos.
- Aplique rate limiting por rota ou globalmente com @fastify/rate-limit.
- Parse cookies e sessões com plugins oficiais em vez de inventar criptografia própria.
Para observabilidade, logs JSON do Pino encaixam bem em Datadog, Grafana Loki ou CloudWatch. Adicione request IDs num hook onRequest e propague para serviços downstream. Instrumentação OpenTelemetry para Fastify já é madura o suficiente para tracing distribuído em arquiteturas multi-serviço.
Faça deploy atrás de proxy reverso (nginx, Caddy ou load balancer na nuvem), rode múltiplos workers Node com cluster ou réplicas em container, e exponha uma rota leve de /health que verifique dependências críticas. Hooks de startup e shutdown do Fastify ajudam a drenar conexões com grace durante deploys.
Quando o Fastify é a escolha certa
Escolha Fastify quando quiser desempenho Node.js com contratos mais claros do que o Express costuma incentivar, sem a cerimônia de um framework completo de DI. Ele se destaca em APIs REST e JSON, camadas BFF na frente de Laravel ou outros backends, webhooks e ferramentas internas onde desenvolvimento guiado por schema acelera iteração.
Vale considerar outra opção se você precisa de ORM integrado e codegen via CLI como premissa central — embora muitas equipes usem Fastify com Prisma, Drizzle ou Knex sem problema — ou se a organização padronizou módulos e decorators NestJS em todos os serviços.
O ponto forte do Fastify é engenharia backend pragmática: schemas explícitos, plugins composáveis, defaults sólidos e espaço para crescer. Comece com um módulo pequeno, invista cedo em definições compartilhadas de schema e tratamento de erros, e você terá uma API que permanece rápida sob carga e compreensível seis meses depois — exatamente o que a maioria dos serviços Node.js em produção precisa.