Boas práticas
Doze decisões que separam uma integração que aguenta produção de uma que aguenta o happy path.
1. Use pelo menos dois provedores
providers: [viaCepProvider, brasilApiProvider, apicepProvider]Um provedor só transforma a instabilidade dele na sua indisponibilidade.
2. Defina timeout por provedor
const providers = [
{ ...viaCepProvider, timeout: 1200 },
{ ...brasilApiProvider, timeout: 1200 },
{ ...apicepProvider, timeout: 1200 },
];Sem timeout, a cauda longa de um provedor vira a latência do seu endpoint.
3. Ligue o circuit breaker
circuitBreaker: { enabled: true, failureThreshold: 3, cooldownMs: 30_000 }Impede que sua aplicação continue batendo em quem já provou estar fora.
4. Retente no máximo uma ou duas vezes
retries: 1,
retryDelay: 300,Mais que isso multiplica latência de pior caso e piora a carga de um provedor já em dificuldade.
5. Limite a taxa
rateLimit: { requests: 60, per: 60_000 }Contém rajada vinda do frontend antes que ela vire bloqueio do provedor.
6. Escolha o cache pelo tempo de vida necessário
| Precisa sobreviver a... | Adaptador |
|---|---|
| só o processo atual | InMemoryCache |
| reload da página | WebStorageCache (ou IndexedDBCache em alto volume) |
| todos os processos do servidor | RedisCache |
| todos os colos de edge | CloudflareKVCache |
import { RedisCache } from "@eusilvio/cep-lookup/cache";
cache: new RedisCache({
client,
ttl: 7 * 24 * 60 * 60_000, // dados de CEP mudam na escala de meses
evictAfter: 30 * 24 * 60 * 60_000, // mantém entradas para o stale-if-error
onError: (error, operation) => logger.warn({ error, operation }, "cep cache degradado"),
})Mantenha evictAfter bem acima de ttl: ttl decide o que é fresco, evictAfter decide o que ainda existe para servir durante uma queda.
7. Trate os erros por código, não por mensagem
INVALID_CEPNOT_FOUNDTIMEOUTRATE_LIMITEDALL_PROVIDERS_FAILEDPROVIDER_UNAVAILABLE
Veja Tratamento de erros.
8. Monitore health e métricas
cep.getProviderHealth();
cep.getProviderMetrics();avgLatencyMs é EWMA (amostras recentes dominam) e p95LatencyMs denuncia a cauda lenta que a média esconde.
9. Degrade para dado velho antes de degradar para erro
cache: new InMemoryCache({ ttl: 10 * 60_000 }),
staleIfError: { maxAgeMs: 24 * 60 * 60_000 }, // nunca sirva dado com mais de 24hEscute cache:stale e alerte se disparar demais: normalmente significa que todos os provedores estão fora.
10. Use cache negativo para entrada sabidamente ruim
negativeCacheTtl: 60_000,Se o produto reenvia o mesmo formulário várias vezes, isso evita repetir buscas que já se sabem 404.
11. Escolha a estratégia de rate limit pelo contexto
strategy: "throw"(padrão) para requisição de usuário: falhe rápido, deixe a UI retentar.strategy: "wait"para jobs e lotes, onde segurar a chamada é melhor que tratarRateLimitError.
12. Cancele requisição de UI obsoleta
const controller = new AbortController();
cep.lookup(valor, { signal: controller.signal });
// depois: controller.abort();Assim uma resposta atrasada nunca sobrescreve o estado atual. Os hooks de React e Vue já fazem isso.
Bônus: valide sem rede antes de gastar rede
import { isCepAllocated, cepMatchesState } from "@eusilvio/cep-lookup/offline";
if (!isCepAllocated(form.cep)) return erro("CEP inexistente.");
if (!cepMatchesState(form.cep, form.uf)) return erro(`CEP não pertence a ${form.uf}.`);Zero latência, zero quota consumida, e pega o erro de digitação mais comum de checkout.