Desenvolvendo clientes de API robustos em TypeScript com Axios

Bruna Pereira
4 min readJul 25, 2023
Prompt: “light background and I magnifying glass for a medium post about observability”

English version here.

Se você usa TypeScript (ou JavaScript) no backend e ainda não tem uma maneira unificada de tratar logs de requisição e resposta, você pode estar perdendo informações valiosas de debug para manter um alto nível de observabilidade no seu sistema.

Boa qualidade de logs nos poupa muito tempo de debug, e esse ganho é exponencial à medida que o tempo passa e o codebase cresce. Por isso, é muito importante o tempo despendido em uma boa configuração de logs dos seus serviços em geral.

O propósito desse artigo é te mostrar como usar uma boa estrutura de logs para requisições a serviços externos. Apesar de o exemplo ser com Axios, eu aplico essa mesma estrutura para qualquer lib ou linguagem que eu venha a usar, pois os princípios são os mesmos.

O que vamos fazer:

  1. Logar todos os requests
  2. Logar todos os responses (falha ou sucesso)
  3. Ser capaz de co-relacionar os logs de request e response.

Apesar de óbvio, eu vejo muitos codebases que não tem uma maneira estruturada de logar. Se cada cliente de API diferente o fizer de uma forma, pode acontecer de faltar algum log, ou alguma informação crucial para o debug daquele problema em específico. E é péssimo descobrir que você tem um log pela metade justamente quando o erro já aconteceu e você está procurando alguma resposta em meio aos logs.

Por isso, com Axios eu gosto de usar os interceptors. Interceptors são funções que vão, como o nome diz, interceptar requests e responses feitos a partir da biblioteca.

Aqui não tem muito mistério, então vou colocar um pedaço do código mostrando como criar os interceptors para sua instância do Axios.

É simples. Ao criar a instância do Axios, você pode designar funções que vão ser executadas sempre antes de cada requisição e depois de cada resposta, com ou sem falha.

No exemplo acima, eu criei um log que vai ser executado em cada um desses pontos. O próximo passo é adicionar aos logs informações que serão úteis para o seu eu do futuro.

No meu caso, eu mantenho um padrão de logs de requisições em todos os meus serviços. Para logs de requisições que estão chegando na minha API, eu coloco a mensagem inbound request e inbound response success (ou failure, se for o caso). Para logs de requisições feitas a partir da minha API, eu uso a nomenclatura outbound request e outbound response success.

Com foco nas requisições que são feitas a partir da minha API (outbound), eu considero relevantes as seguintes informações:

Para requisições: URL base, path, método, corpo do request, parâmetros do request e headers.
Para respostas: URL base, path, status da resposta, body e headers.

E uma informação imprescindível: um id que co-relaciona requests e responses. Sabe, Javascript, tudo é assíncrono, nada garante a ordem dos logs. Se você tiver múltiplos requests sendo feitos ao mesmo tempo, você não vai fazer ideia de qual log de requisição corresponde a qual log de resposta apenas olhando para eles.

Por isso, tanto no request quanto no response, coloco também um axiosId, que nada mais é do que um uuid gerado a partir da requisição, e replicado na resposta.

Veja como fica:

Apesar do código auto-explicativo, é importante mencionar dois pontos:

  1. Eu precisei criar uma versão estendida do AxiosConfig, para adicionar metadados à requisição, que vão estar disponíveis também na resposta. Não tente fazer malabarismos e criar variáveis globais para isso.
  2. Para acessar informações da requisição, no momento da resposta, nunca utilize axiosError.response.request. Além de incompleto, ele pode te guiar para problemas de dependência circular no momento do log.

Apenas com essas linhas de código, você já está preparada(o) para ter acesso a todos os dados relevantes das suas requisições. Veja um exemplo de como ficou estruturado o meu log no Google Cloud Logging:

Logs estruturados

Em um próximo artigo eu vou mostrar uma maneira estruturada de criar instâncias diferentes para cada Client garantindo que todos eles estarão cobertos com os interceptors, e consequentemente, com logs de requisição e resposta.

Você acha que faltou alguma informação importante nos meus logs? Me conta aqui nos comentários :)

Até!

--

--