Developing robust API Clients in Typescript with Axios

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

Versão em portugês aqui.

If you use TypeScript (or JavaScript) on the backend and still don’t have a unified way of handling request and response logs, you might be missing valuable debugging information to maintain a high level of observability in your system.

Good quality logs save us a lot of debugging time, and this gain becomes exponential as time goes by and the codebase grows. Therefore, the time spent on configuring logs for your services in general is very important.

The purpose of this article is to show you how to use a good log structure for requests to external services. Although the example is with Axios, I apply the same structure to any library or language I use, as the principles remain the same.

What we are going to do:

  • Log all requests
  • Log all responses (failure or success)
  • Be able to correlate request and response logs.

Although obvious, I see many codebases that do not have a structured way of logging. If each different API client does it differently, it may happen that some logs are missing, or some crucial information for debugging that specific problem is missing. And it’s terrible to discover that you have a half-logged situation just when the error has already occurred and you’re looking for some answers in the midst of the logs.

That’s why, with Axios, I like to use interceptors. Interceptors are functions that will intercept requests and responses made from the library, as the name implies.

Here, there’s not much mystery, so I’ll show you a piece of code showing how to create the interceptors for your Axios instance.

It’s simple. When creating the Axios instance, you can assign functions that will be executed before each request and after each response, with or without failure.

In the example above, I created a log that will be executed at each of these points. The next step is to add information to the logs that will be useful for your future self.

In my case, I maintain a standard for request logs in all my services. For request logs coming to my API, I use the message inbound request and inbound response success (or failure, if applicable). For request logs made from my API, I use the nomenclature outbound request and outbound response success.

Focusing on requests made from my API (outbound), I consider the following information relevant:

For requests: Base URL, path, method, request body, request parameters, and headers. For responses: Base URL, path, response status, body, and headers.

And an essential piece of information: an ID that correlates requests and responses. You know, in JavaScript, everything is asynchronous, and nothing guarantees the order of the logs. If you have multiple requests being made at the same time, you won’t have any idea which request log corresponds to which response log just by looking at them.

So, both in the request and the response, I also include an axiosId, which is nothing more than a UUID generated from the request and replicated in the response.

Here’s how it looks:

Despite the self-explanatory code, it’s essential to mention two points:

  • I needed to create an extended version of AxiosConfig to add metadata to the request, which will also be available in the response. Don’t try to juggle and create global variables for this.
  • To access request information at the time of the response, never use axiosError.response.request. Besides being incomplete, it can lead you to problems with circular dependencies at the time of logging.

With just these lines of code, you are now prepared to have access to all relevant data from your requests. Here’s an example of how my log is structured in Google Cloud Logging:

Structured logging

In an upcoming article, I will show a structured way to create different instances for each Client, ensuring that all of them will be covered with interceptors and consequently, request and response logs.

Do you think any important information is missing in my logs? Tell me here in the comments :)

See you!

--

--