O Fastify é um dos frameworks Node.js mais rápidos disponíveis, projetado para alto desempenho com baixo overhead. Sua arquitetura baseada em plugins e decorators permite encapsular a integração com a API de CPF de forma modular e reutilizável. Com schemas JSON integrados, o Fastify valida parâmetros de entrada e serializa respostas automaticamente, sem configuração extra.
1. Pré-requisitos
-
Node.js 18+ instalado.
-
Fastify instalado (
npm install fastify). -
Pacotes adicionais:
@fastify/env,@fastify/rate-limit. -
Uma conta na CPFHub.io
2. Instale as dependências
npm init -y
npm install fastify @fastify/env @fastify/rate-limit
3. Configure as variáveis de ambiente
Crie o arquivo .env:
CPFHUB_API_KEY=SUA_CHAVE_DE_API
CPFHUB_BASE_URL=https://api.cpfhub.io
CPFHUB_TIMEOUT=5000
PORT=3000
4. Plugin de integração com a CPFHub.io
O plugin encapsula toda a lógica de integração e expõe um decorator na instância do Fastify:
// plugins/cpfhub.js
const fp = require('fastify-plugin');
async function cpfhubPlugin(fastify, options) {
const { apiKey, baseUrl, timeout } = options;
async function consultarCpf(cpf) {
const cpfLimpo = cpf.replace(/\D/g, '');
if (cpfLimpo.length !== 11) {
throw fastify.httpErrors.badRequest('CPF deve conter 11 dígitos');
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(`${baseUrl}/cpf/${cpfLimpo}`, {
method: 'GET',
headers: {
'x-api-key': apiKey,
'Accept': 'application/json'
},
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
const mensagens = {
400: 'CPF com formato inválido',
401: 'Chave de API inválida ou ausente',
404: 'CPF não encontrado'
};
const msg = mensagens[response.status] || `Erro HTTP ${response.status}`;
if (response.status === 401) throw fastify.httpErrors.unauthorized(msg);
if (response.status === 404) throw fastify.httpErrors.notFound(msg);
throw fastify.httpErrors.badRequest(msg);
}
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
if (error.statusCode) throw error; // Já é um httpError
if (error.name === 'AbortError') {
throw fastify.httpErrors.gatewayTimeout('Timeout na consulta de CPF');
}
throw fastify.httpErrors.badGateway('Falha na conexão com a API de CPF');
}
}
// Decorator: disponibiliza cpfhub.consultarCpf em todas as rotas
fastify.decorate('cpfhub', { consultarCpf });
}
module.exports = fp(cpfhubPlugin, {
name: 'cpfhub',
dependencies: ['@fastify/sensible']
});
O uso de fastify-plugin (fp) garante que o decorator seja acessível em todo o escopo da aplicação, não apenas no contexto do plugin.
5. Schemas de validação e serialização
Defina schemas JSON para validar parâmetros de entrada e serializar a resposta:
// schemas/cpf.js
const cpfParamsSchema = {
type: 'object',
required: ['cpf'],
properties: {
cpf: {
type: 'string',
pattern: '^\\d{11}$',
description: 'CPF com 11 dígitos numéricos'
}
}
};
const cpfResponseSchema = {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
cpf: { type: 'string' },
name: { type: 'string' },
nameUpper: { type: 'string' },
gender: { type: 'string' },
birthDate: { type: 'string' },
day: { type: 'integer' },
month: { type: 'integer' },
year: { type: 'integer' }
}
}
}
};
module.exports = { cpfParamsSchema, cpfResponseSchema };
6. Aplicação completa
// app.js
require('dotenv').config();
const fastify = require('fastify')({
logger: true
});
const { cpfParamsSchema, cpfResponseSchema } = require('./schemas/cpf');
async function build() {
// Plugins essenciais
await fastify.register(require('@fastify/sensible'));
// Rate limiting
await fastify.register(require('@fastify/rate-limit'), {
max: 30,
timeWindow: '1 minute'
});
// Plugin CPFHub
await fastify.register(require('./plugins/cpfhub'), {
apiKey: process.env.CPFHUB_API_KEY,
baseUrl: process.env.CPFHUB_BASE_URL,
timeout: parseInt(process.env.CPFHUB_TIMEOUT || '5000', 10)
});
// Rota de consulta de CPF
fastify.get('/api/cpf/:cpf', {
schema: {
params: cpfParamsSchema,
response: {
200: cpfResponseSchema
}
}
}, async (request, reply) => {
const { cpf } = request.params;
const resultado = await fastify.cpfhub.consultarCpf(cpf);
return resultado;
});
// Health check
fastify.get('/health', async () => {
return { status: 'ok' };
});
return fastify;
}
build().then((app) => {
app.listen({ port: parseInt(process.env.PORT || '3000', 10), host: '0.0.0.0' }, (err) => {
if (err) {
app.log.error(err);
process.exit(1);
}
});
});
7. Teste com cURL
node app.js
curl -X GET http://localhost:3000/api/cpf/12345678900 \
-H "Accept: application/json"
Resposta esperada:
{
"success": true,
"data": {
"cpf": "12345678900",
"name": "João da Silva",
"nameUpper": "JOÃO DA SILVA",
"gender": "M",
"birthDate": "15/06/1990",
"day": 15,
"month": 6,
"year": 1990
}
}
Se o CPF não tiver 11 dígitos, o schema de validação retorna automaticamente um erro 400 antes de executar o handler.
8. Plugin de cache
Crie um plugin de cache para evitar consultas repetidas:
// plugins/cpfCache.js
const fp = require('fastify-plugin');
async function cpfCachePlugin(fastify, options) {
const cache = new Map();
const ttl = options.ttl || 3600000; // 1 hora padrão
fastify.decorate('cpfCache', {
get(cpf) {
const entry = cache.get(cpf);
if (entry && Date.now() - entry.timestamp < ttl) {
return entry.data;
}
cache.delete(cpf);
return null;
},
set(cpf, data) {
cache.set(cpf, { data, timestamp: Date.now() });
}
});
}
module.exports = fp(cpfCachePlugin, { name: 'cpfCache' });
Use na rota:
fastify.get('/api/cpf/:cpf', {
schema: { params: cpfParamsSchema, response: { 200: cpfResponseSchema } }
}, async (request, reply) => {
const { cpf } = request.params;
// Verificar cache
const cached = fastify.cpfCache.get(cpf);
if (cached) return cached;
// Consultar API
const resultado = await fastify.cpfhub.consultarCpf(cpf);
// Salvar no cache
if (resultado.success) {
fastify.cpfCache.set(cpf, resultado);
}
return resultado;
});
9. Hook para logging de consultas
Use hooks do Fastify para registrar cada consulta:
fastify.addHook('onResponse', (request, reply, done) => {
if (request.url.startsWith('/api/cpf/')) {
fastify.log.info({
cpf: request.params.cpf,
statusCode: reply.statusCode,
responseTime: reply.elapsedTime
}, 'Consulta de CPF registrada');
}
done();
});
10. Boas práticas
-
Plugins -- Encapsule a integração com a CPFHub.io em um plugin reutilizável. O padrão de plugin do Fastify facilita testes e compartilhamento.
-
Decorators -- Exponha a função de consulta como decorator para acesso em qualquer rota sem imports manuais.
-
Schemas -- Use JSON Schema para validar parâmetros de entrada e serializar respostas automaticamente. O Fastify usa os schemas para otimizar a serialização.
-
Timeout -- O
AbortControllercom 5 segundos é configurado no plugin e evita consultas pendentes. -
Rate limit local -- O
@fastify/rate-limitprotege o endpoint de abusos internos. A CPFHub.io não bloqueia ao atingir o limite do plano — cobra R$0,15 por consulta adicional (Gratuito: 50/mês, Pro: 1.000/mês por R$149). -
Sensible -- O
@fastify/sensibleadiciona helpers comohttpErrorspara tratamento de erros padronizado.
Perguntas frequentes
Por que usar plugins e decorators em vez de importar a função diretamente nas rotas?
Plugins e decorators seguem o modelo de encapsulamento do Fastify, conforme descrito na documentação oficial do framework. A integração fica isolada em um único ponto, o que facilita testes unitários, substituição do provedor de CPF e reuso da lógica em múltiplas rotas sem duplicação de código.
O @fastify/rate-limit e o rate limit da CPFHub.io são a mesma coisa?
Não. O @fastify/rate-limit controla quantas requisições chegam ao seu servidor por minuto — protegendo contra abusos dos seus próprios usuários. Já a CPFHub.io controla quantas consultas sua chave de API realiza por mês. Os dois limites são independentes e complementares.
Como o plugin lida com timeout na chamada à API de CPF?
O AbortController cancela a requisição após o tempo configurado (padrão: 5 segundos). Quando isso acontece, o plugin lança um gatewayTimeout (503), informando ao cliente que o serviço externo não respondeu a tempo, sem deixar a conexão pendente no servidor.
O cache em memória (Map) é suficiente para produção?
Para volumes baixos, sim. Para aplicações com múltiplas instâncias ou alto tráfego, substitua o Map por Redis ou Memcached para compartilhar o cache entre processos. O padrão de plugin do Fastify torna essa substituição simples: basta criar um novo plugin que implemente a mesma interface get/set.
Conclusão
O Fastify com plugins e decorators oferece a melhor combinação de performance e organização para consumir a API da CPFHub.io
Cadastre-se em cpfhub.io
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



