API de órgão público: separe teste e produção antes do primeiro dado real
Configure ambientes que não se misturam, recuse URL e credencial trocadas na inicialização, trate instabilidade com espera crescente e registre protocolo sem logar dado de cidadão.
O escritório de contabilidade integrou a consulta de situação cadastral de uma API de governo copiando a URL do exemplo da documentação. Funcionou de primeira. Três meses depois, alguém percebeu que todas as consultas de produção estavam indo para o ambiente de demonstração, que devolve dados fictícios fixos. Os relatórios dos clientes tinham sido montados com resposta de mentira. API de órgão público tem ambiente de teste e de produção, com endereços e credenciais diferentes, e o código precisa recusar a mistura.
A resposta curta: coloque URL, credenciais e ambiente em variáveis de configuração, valide na inicialização que a URL pertence ao ambiente declarado, use tempo limite e novas tentativas com espera crescente só para erros transitórios, guarde o protocolo de cada chamada e nunca registre em log o documento do cidadão por inteiro.
Por que o exemplo do manual não é o endereço de produção
Documentações de API de governo, como as do catálogo do Serpro, costumam oferecer um ambiente de demonstração ou de testes, com credenciais públicas ou de avaliação e dados fictícios. Isso é ótimo para aprender o formato. O problema é que o exemplo do manual quase sempre aponta para esse ambiente, e copiar o exemplo leva o endereço de teste para o código.
O erro contrário é pior. Uma credencial de produção numa máquina de desenvolvimento faz o teste automatizado consumir cota paga e enviar dados de pessoas reais para logs locais. Os dois acidentes têm a mesma causa: o ambiente não é uma decisão explícita do sistema, é um efeito colateral de qual URL alguém colou.
Antes da URL, há o cadastro e o contrato; o guia API de governo pede cadastro cobre essa etapa. Este guia começa quando você já tem as credenciais.
Como separar os ambientes no código
- Tire URL e credenciais do código. Variáveis de ambiente ou cofre de segredos, nunca no repositório. A imagem do container também não leva segredo, como mostra o guia de segredo fora da imagem.
- Declare o ambiente explicitamente. Uma variável como
ORGAO_AMBIENTEcom valortesteouproducao. - Valide a combinação na inicialização. Cada ambiente tem uma lista de hosts permitidos. Se a URL não bate, a aplicação nem sobe.
- Guarde certificado digital por ambiente. Se a API exige certificado, o de produção fica só no servidor de produção, com acesso restrito.
- Mostre o ambiente na tela. Uma faixa "AMBIENTE DE TESTE" no sistema interno evita que alguém trabalhe achando que é produção.
// config-orgao.js
const AMBIENTES = {
teste: ['api-teste.orgao.exemplo.gov.br'],
producao: ['api.orgao.exemplo.gov.br']
};
export function carregarConfigOrgao(env = process.env) {
const ambiente = env.ORGAO_AMBIENTE;
if (!Object.hasOwn(AMBIENTES, ambiente)) {
throw new Error('ORGAO_AMBIENTE precisa ser "teste" ou "producao"');
}
if (!env.ORGAO_API_URL || !env.ORGAO_CLIENT_ID || !env.ORGAO_CLIENT_SECRET) {
throw new Error('Faltam ORGAO_API_URL, ORGAO_CLIENT_ID ou ORGAO_CLIENT_SECRET');
}
const url = new URL(env.ORGAO_API_URL);
if (url.protocol !== 'https:' || !AMBIENTES[ambiente].includes(url.hostname)) {
throw new Error('A URL ' + url.hostname + ' não pertence ao ambiente ' + ambiente);
}
return {
ambiente,
baseUrl: url.origin,
clientId: env.ORGAO_CLIENT_ID,
clientSecret: env.ORGAO_CLIENT_SECRET
};
}
Os hosts do exemplo são fictícios; os reais estão na documentação da API que você contratou. O ponto é a regra: a aplicação falha na inicialização, e não na primeira consulta de um cliente, quando alguém troca uma variável errada.
Como tratar instabilidade sem parecer um ataque
Serviços de governo têm janelas de manutenção e picos de uso, e algumas APIs limitam requisições por período. Repetir a chamada dez vezes no mesmo segundo quando ela falha aumenta a carga justamente na hora ruim e pode fazer o seu acesso ser bloqueado. O certo é tentar poucas vezes, com espera crescente e um componente aleatório, e só para erros que podem passar sozinhos:
// retry-orgao.js
const esperar = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
function atraso(tentativa) {
const base = 1000 * 2 ** (tentativa - 1);
return base / 2 + Math.random() * (base / 2);
}
const TRANSITORIOS = new Set([429, 502, 503, 504]);
export async function chamarComRetry(fazerRequisicao, maxTentativas = 4) {
for (let tentativa = 1; ; tentativa++) {
let resp;
try {
resp = await fazerRequisicao(AbortSignal.timeout(15000));
} catch (erro) {
if (tentativa >= maxTentativas) throw erro;
await esperar(atraso(tentativa));
continue;
}
if (!TRANSITORIOS.has(resp.status) || tentativa >= maxTentativas) return resp;
const segundos = Number(resp.headers.get('retry-after'));
await esperar(segundos > 0 ? segundos * 1000 : atraso(tentativa));
}
}
Erros 400, 401 e 404 voltam na hora, porque repetir não muda o resultado. O cabeçalho Retry-After, quando vem em segundos, é respeitado. Use isso para consultas; para operações que gravam algo no órgão, só repita se a API documentar como evitar duplicidade. Se a falha persistir, a consulta vai para uma fila e o sistema avisa que o resultado sai mais tarde. O guia de timeout antes do infinito explica por que o tempo limite vem antes de tudo isso.
Log, protocolo e dado do cidadão
A resposta de uma API de governo frequentemente traz um identificador da requisição ou um protocolo. Guarde junto do registro da consulta: é o que o suporte do órgão vai pedir quando você abrir um chamado. O que não vai para o log é o corpo com CPF, nome e situação da pessoa. Registre o mínimo para diagnóstico, com o documento mascarado:
function mascararCpf(cpf) {
const d = String(cpf).replace(/\D/g, '');
if (d.length !== 11) return '[cpf inválido]';
return '***.' + d.slice(3, 6) + '.' + d.slice(6, 9) + '-**';
}
// log: { ambiente: 'producao', rota: '/consulta', status: 200,
// protocolo: 'abc123', cpf: mascararCpf(cpf), ms: 412 }
O campo ambiente no log parece redundante até o dia em que alguém procura uma consulta de produção nos logs de teste. Mais cuidados de registro estão em log sem senha.
Erros comuns
- Copiar a URL do exemplo para o código. A produção passa a consultar o ambiente de demonstração e recebe dados fictícios sem erro nenhum.
- Usar credencial de produção no desenvolvimento. Teste automatizado consome cota paga e espalha dado real por máquinas locais.
- Repetir a chamada imediatamente em loop. A carga sobe na hora da instabilidade e o acesso pode ser bloqueado.
- Repetir erros que não são transitórios. Um 401 repetido quatro vezes continua 401 e só atrasa o aviso.
- Logar o corpo da resposta. CPF e situação cadastral de pessoas ficam em ferramentas de log com acesso amplo.
- Não guardar o protocolo. O suporte do órgão não consegue localizar a requisição que deu problema.
Checklist
- URL, credenciais e ambiente vêm de configuração, não do código.
- A aplicação recusa subir se a URL não pertence ao ambiente declarado.
- Credenciais e certificado de produção existem só no servidor de produção.
- Toda chamada tem tempo limite.
- Novas tentativas só para erros transitórios, com espera crescente.
- O protocolo ou identificador da requisição é guardado com cada consulta.
- Logs têm o ambiente e mascaram documentos pessoais.
- O sistema interno mostra claramente quando está em ambiente de teste.
Para o raciocínio geral de ambientes, veja ambiente de teste que parece produção. E agora procure no seu código: a URL de produção do órgão está numa variável validada na inicialização, ou ainda está no meio de uma função, copiada do manual?
Referência: https://apicenter.estaleiro.serpro.gov.br/documentacao/