Skip to Content

Lesson 6: Advanced API Interactions with axios

Workplace Context

While Node’s built-in fetch is capable, real-world applications often require more complex interactions and a more streamlined development experience. Your team needs to send structured data to an external API, handle authentication for every request, and manage different API configurations cleanly. For these tasks, a dedicated HTTP client library like axios is the industry standard.


Learning Objectives

By the end of this lesson, you will be able to:

  • Explain the advantages of using axios over fetch.
  • Refactor a fetch request to use axios.
  • Construct and send POST requests with a data payload using axios.
  • Create pre-configured axios instances for specific APIs using axios.create().
  • Use interceptors to globally modify requests and responses.

axios vs. fetch: Why Use a Library?

axios is a very popular, Promise-based HTTP client. While it does the same fundamental job as fetch - making HTTP requests - it offers several quality-of-life improvements that developers prefer for professional projects:

Featurefetch()axios
JSON DataRequires two steps: one to get the response, a second to parse the body with response.json().Automatic. axios automatically stringifies outgoing data and parses incoming data. The result is in response.data.
Error HandlingOnly rejects on network errors. For “bad” HTTP statuses (like 404 or 500), you must manually check response.ok or response.status.Unified. Rejects on both network errors AND bad HTTP statuses. This lets you catch all errors in a single .catch() block.
Request ConfigurationConfiguration (headers, method, body) is passed in a second, sometimes verbose, options object.Provides clear, top-level methods like axios.post() and axios.delete() that are more intuitive.
Request TimeoutSupported via an AbortController, which can be complex to set up.Supported with a simple timeout property in the configuration.

To get started, you must first install axios: npm install axios

Refactoring fetch to axios

Let’s refactor the GET /users example from the previous lesson. Notice how axios simplifies the process by removing the need for the .json() parsing step and unifying error handling.

server.js
const express = require('express'); const axios = require('axios'); // 1. Import axios const app = express(); const port = 3000; app.get('/users', async (req, res) => { try { // 2. Await the axios GET request. const response = await axios.get('https://jsonplaceholder.typicode.com/users'); // 3. The data is already parsed and available in `response.data`. res.json(response.data); } catch (error) { // 4. This single catch block handles everything: // - Network errors (can't connect) // - HTTP status errors (404, 500, etc.) if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx console.error('API Error:', error.response.status, error.response.data); res.status(error.response.status).json({ message: 'Error fetching data from external API.' }); } else { // Something happened in setting up the request that triggered an Error console.error('Network Error:', error.message); res.status(500).json({ message: 'A network error occurred.' }); } } }); app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); });

Making POST Requests with axios

Sending data is where axios truly shines in its simplicity. To make a POST request, you use axios.post(). The first argument is the URL, and the second is the data payload (as a JavaScript object). axios handles converting this object to a JSON string for you.

server.js
const express = require('express'); const axios = require('axios'); const app = express(); const port = 3000; // Middleware to parse the body of OUR server's incoming requests app.use(express.json()); app.post('/posts', async (req, res) => { try { // The data we want to send, perhaps from our own client const newPostData = req.body; // The second argument to axios.post is the request body. // axios automatically sets the Content-Type header to application/json. const response = await axios.post( 'https://jsonplaceholder.typicode.com/posts', newPostData ); // Send back the newly created post from the external API res.status(201).json(response.data); } catch (error) { console.error('Error creating post:', error.message); res.status(500).json({ message: 'Failed to create post.' }); } }); app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); });

Creating a Configured Instance

In a real application, you’ll often make many requests to the same API. Repeating the base URL and common headers (like an Authorization token) in every single call is repetitive and error-prone.

axios solves this with axios.create(), which lets you create a new, pre-configured instance of the axios client.

Use Case: Imagine you’re building a service that heavily interacts with the GitHub API. You can create a dedicated githubClient for all your requests.

api/githubClient.js
const axios = require('axios'); const githubClient = axios.create({ baseURL: 'https://api.github.com', headers: { 'Accept': 'application/vnd.github.v3+json', // Note: In a real app, this token would come from an environment variable! 'Authorization': 'token YOUR_PERSONAL_ACCESS_TOKEN' } }); module.exports = githubClient;

Now, in other parts of your application, you can use this instance without repeating the configuration:

server.js
// ... other requires const githubClient = require('./api/githubClient'); app.get('/repos/:username', async (req, res) => { try { // No need to write the full URL! Just the endpoint. // Headers are also automatically included. const response = await githubClient.get(`/users/${req.params.username}/repos`); res.json(response.data); } catch (error) { // ... error handling } });

Using Interceptors

Interceptors are one of the most powerful features of axios. They allow you to run your code or modify requests and responses globally before they are sent or handled by then or catch.

Request Interceptors

A request interceptor runs before a request is sent. It’s perfect for tasks that need to happen on every request, like logging or dynamically adding a header.

// Add a request interceptor to our instance githubClient.interceptors.request.use(config => { // This function runs before any request is sent using githubClient console.log(`Sending request to: ${config.baseURL}${config.url}`); // You must always return the config object, otherwise the request will fail return config; }, error => { // Handle request error return Promise.reject(error); });

Response Interceptors

A response interceptor runs before the then or catch of your request is executed. It allows you to transform the response data globally or handle errors in a centralized way.

// Add a response interceptor githubClient.interceptors.response.use(response => { // Any status code that lie within the range of 2xx cause this function to trigger console.log('Received successful response!'); // You can transform the response data here if needed return response; }, error => { // Any status codes that falls outside the range of 2xx cause this function to trigger console.error('An error occurred with the response.'); // You can handle errors globally here return Promise.reject(error); });

Interceptors are advanced tools for creating clean, non-repetitive, and maintainable API interaction code.


Activities

Activity 1: Create an API Client

  1. Create a new file called apiClient.js.
  2. Inside this file, use axios.create() to make a pre-configured instance for the JSONPlaceholder API (https://jsonplaceholder.typicode.com).
  3. Export the created instance.
  4. In your server.js, import the client and use it to refactor your /users route from the first example.

Activity 2: Add a Logger Interceptor

  1. In apiClient.js, add a request interceptor to your axios instance.
  2. The interceptor should simply log the method and URL of the outgoing request (e.g., Requesting: GET /users).
  3. Test your /users route again and check your console to see the interceptor’s log message.

Knowledge Check

What is the main benefit of using axios.create()?

  • Select an answer to view feedback.

What is a common use case for a request interceptor?

  • Select an answer to view feedback.

Summary

In this lesson, you took a deep dive into the advanced features of axios that make it an industry standard. You learned how to create clean, reusable API clients with axios.create() and how to globally handle requests and responses with interceptors. These patterns are essential for writing scalable, maintainable code that interacts with external services.


References