Plugins do Fastify
Organize apps Fastify com plugins encapsulados, autoload e decoradores compartilhados.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
import Fastify from "fastify";
import fp from "fastify-plugin";
import autoload from "@fastify/autoload";
import { join } from "node:path";
const app = Fastify({ logger: true });
// Decorador compartilhado (quebra a encapsulação intencionalmente)
await app.register(fp(async (fastify) => {
fastify.decorate("db", { query: async (sql: string) => [] });
}));
// Autoload de todos os plugins em ./plugins e rotas em ./routes
await app.register(autoload, { dir: join(import.meta.dirname, "plugins") });
await app.register(autoload, { dir: join(import.meta.dirname, "routes") });
await app.listen({ port: 3000 });Quando usar isso: Qualquer app Fastify além de um único arquivo. Plugins são a unidade primária de modularidade.
Exemplo de Trabalho
src/
app.ts
plugins/
auth.ts
database.ts
routes/
users.ts
health.ts
// plugins/database.ts
import fp from "fastify-plugin";
export default fp(async (fastify) => {
const pool = { query: async (sql: string) => [{ id: 1 }] };
fastify.decorate("db", pool);
fastify.addHook("onClose", async () => {
// fechar conexões do pool
});
});
// routes/users.ts
import { FastifyPluginAsync } from "fastify";
const users: FastifyPluginAsync = async (fastify) => {
fastify.get("/", async () => {
return fastify.db.query("SELECT * FROM users");
});
};
export default users;
// app.ts
import Fastify from "fastify";
import autoload from "@fastify/autoload";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const app = Fastify({ logger: true });
await app.register(autoload, { dir: join(__dirname, "plugins") });
await app.register(autoload, { dir: join(__dirname, "routes"), options: { prefix: "/api" } });O que isso demonstra:
fastify-pluginenvolve plugins compartilhados para escopo global@fastify/autoloaddescobre plugins por convenção de diretório- Hook
onClosepara limpeza no desligamento - Plugins de rota montados com o prefixo
/api
Mergulho Profundo
Como Funciona
register(plugin)cria um novo contexto de encapsulamento- Decoradores, hooks e rotas dentro de um plugin são invisíveis para plugins irmãos
fastify-plugin(fp) eleva um plugin para o escopo pai- Autoload lê o diretório e registra cada arquivo como um plugin
Regras de Encapsulamento
| Padrão | Escopo | Usar para |
|---|---|---|
register Simples | Apenas escopo filho | Módulos de funcionalidade |
fastify-plugin | Escopo pai | DB, auth, config |
Opção prefix | Prefixo de URL | Agrupamento de rotas |
Hook onClose | Limpeza | Pools de conexão |
Mesclagem de Declaração TypeScript
import "fastify";
declare module "fastify" {
interface FastifyInstance {
db: { query: (sql: string) => Promise<unknown[]> };
}
}Armadilhas
- Decorador não visível nas rotas - registrado em um plugin irmão sem
fp. Correção: envolva comfastify-plugin. - Dependências circulares de plugins - plugin A registra B que registra A. Correção: extraia dependências compartilhadas para um terceiro plugin.
- Ordem de autoload é a ordem do sistema de arquivos - o plugin de autenticação pode carregar depois das rotas. Correção: use os diretórios
plugins/eroutes/; o diretório de plugins carrega primeiro. - Plugin assíncrono sem await - as rotas registram antes que o DB esteja pronto. Correção:
await app.register(dbPlugin)antes das rotas. - Limpeza
onCloseausente - vazamento de pool de conexão no SIGTERM. Correção: feche os pools emonClose. - Exportação padrão vs nomeada - autoload espera uma função assíncrona
export default. Correção: siga a convenção.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Registro manual em app.ts | Apps pequenos (< 5 plugins) | Código em crescimento |
| Módulos NestJS | Precisa de DI, decoradores, guards | Quer um framework mínimo |
| Express Router | Códigobase Express | Projeto Fastify |
| Monolito de arquivo único | Spike/protótipo | Serviço de produção |
FAQs
Qual a diferença entre plugin e rota?
Ambos usam register. Rotas são plugins que definem apenas endpoints. Plugins podem adicionar decoradores, hooks e plugins filhos.
Como o autoload determina a ordem?
Alfabética por nome de arquivo. Prefixe os arquivos com números (01-database.ts) para controlar a ordem, se necessário.
Plugins podem ter opções?
Sim. fastify.register(plugin, { prefix: "/api", dbUrl: "..." }). Acesse via fastify-plugin opts ou closure.
Como testar um plugin isoladamente?
Crie uma instância Fastify() pura, register o plugin, use inject(). Nenhuma porta é necessária.
Cada funcionalidade deve ser um plugin?
Sim. Um plugin por domínio (usuários, pedidos, faturamento) com suas próprias rotas, hooks e esquemas.
Como compartilhar esquemas entre plugins?
Registre com fastify.addSchema() em um plugin de esquemas carregado primeiro, depois use $ref nos esquemas de rota.
Autoload funciona com TypeScript?
Use tsx ou compile para JS primeiro. Autoload carrega arquivos .js da saída de build em produção.
Como isso se compara aos módulos NestJS?
Plugins Fastify são mais leves, sem container DI. Módulos NestJS adicionam provedores, imports e exports. Veja Noções Básicas de NestJS.
Relacionados
- Noções Básicas de Fastify - introdução ao encapsulamento
- Validação de Esquema JSON - esquemas compartilhados
- Logging com Pino - plugin de logging
- Testando Apps Fastify - testes de isolamento de plugin
- Melhores Práticas do Fastify - checklist da seção
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (LTS Ativo), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.