Easier API calls with a wrapper around Axios

When writing separate frontend and backend applications you have to come up with a way of communicating between them by sending some type of requests. Requests on the frontend application can easily become a big and ugly chunk of code that just can’t be reused between modules.

Easier API calls with a wrapper around Axios

Keep reading and discover how did we come up with a solution that can simplify that problem.

The most common way to create requests from the frontend application is to use Axios. As per official Axios documentation, Axios is a promise-based HTTP Client for node.js and the browser. On the server side, it uses the native node.js  http module, while on the client (browser) it uses XMLHttpRequests.

Good thing is that Axios is fairly simple to use. The bad thing is that creating API calls can get a bit messy if you add some extra configuration. Especially if you repeat that configuration from file to file.

Here we’ve come up with the class-based solution for creating an Axios instance that has a single point (file) for configuration so we can toss out the messy part of our Axios usage and keep the simple part of usage only.

Using Axios

Using Axios in a React application is fairly easy. To use it, you just need to import it, and you’re already set.

import React, { useState, useEffect } from 'react';
import axios from "axios";

const RandomUserName: React.FC = () => {
  const [user, setUser] = useState()

	async function getRandomUser() {
	  try {
	    const data = await axios.get("/api/users/random_user");
	    setUser(data)
	  } catch(err) {
	    console.log("error: ", err);
	  }
	}

  useEffect(getRandomUser, []);

  if (!user || !user.name) return null;

  return <p>{user.name}</p>
}

As you can see in my very simple example, it’s fairly easy to use. But, if we are going to use it in a real-world application, things can get a little bit more complicated. Not in a way that it’s complicated to use Axios itself, but the way to configure it and to keep it consistent for each use. Especially in some big applications where we have an API call on every page we land.

import React from 'react';
import axios from "axios";
import { FormProvider, useForm } from 'react-hook-form';

const CreateRandomUserForm: React.FC = () => {
  const form = useForm<User>();
  
	async function createRandomUser(randomUser: User) {
	  try {
	    await axios.post("/api/users/random_user", randomUser, {
        baseUrl: '<https://some-base-url>',
        beforeRedirect: someBeforeRedirectFunction,
				headers: {
          'Content-Type': 'application/json',
          Authorization: `some token`,
        },
				... // you get the point
			});
	  } catch(err) {
	    console.log("error: ", err);
	  }
	}

  return (
  	<FormProvider {...form}>
		<form onSubmit={form.handleSubmit(createRandomUser)}>
			<UserFormInputs />
		</form>
	</FormProvider>
  );
}

In this “complicated” example, you can see that we’ve added a configuration. Now imagine if you have more files with API calls that need to add that type of configuration + some interceptors. Nonetheless, our code can become bloated if configured incorrectly.

React-wise, we can create a shared hook that’s creating an instance of the Axios client and then configure it to keep it consistent for every API request. That’s something similar we’ve come up with.

Our Axios client

We had a problem with API calls in one of the applications we are developing. They were a bit complicated and bloated our code because of how they were set up in the beginning. We saw that and we wanted to refactor it. We have developed a class-based solution that can now be reused with almost every project, regardless of the framework.

We’ve come up with axios-http-client. It’s fairly simple to use. You just have to create some shareable class, configure it if needed and you’re ready to use it across your project. Some general example:

// http.ts
import { HttpClient, HttpClientConstructor, HttpClientAxiosConfig } from 'axios-http-wrapper';

// Type it for autocomplete.
const httpClientConstructor: HttpClientConstructor = {
  baseUrl: '<http://some.api.url>',
}

const config: HttpClientAxiosConfig<BaseModel> = {
  headers: {
    accept: 'application/json',
  };
}

// HttpClient class can have some BaseModel passed as a generic.
export const MyHttpClient extends HttpClient<BaseModel> {
  constructor(private endpoint?: string) {
    super({
      baseUrl: '<http://some.api.url>',
      endpoint: this.endpoint,
    }, config);

    // If you need to configure some axios interceptors, you can also do this here.
    this.client.interceptors.response.use(
      (response) => response,
      (error) => error,
    );
  }
}

More examples of how to use it can be found on our GitHub and our npm. In this documentation, you can also find a simplified way to use it in a React application as a hook.

axios-http-wrapper
HTTP class wrapper around axios for making api calls easier.. Latest version: 1.1.1, last published: 2 hours ago. Start using axios-http-wrapper in your project by running `npm i axios-http-wrapper`. There are no other projects in the npm registry using axios-http-wrapper.

The main reason why we created this was to simplify the creation of API requests in the application. To make our code cleaner and easier to understand. Also, to make API requests more consistent and less error-prone.

We’ve left the Axios interceptors intact so if you need to have some custom logic before sending a request, or getting a response, you can customize it as you want. Also, if you have a custom error handling, you can add that logic here.

Conclusion

Creating API calls can make a real mess in your application if your mechanism for API calls isn’t set up right. For small applications, it’s not big of a deal, but when you’re developing some bigger applications, it can make a huge difference.

If you ever find yourself in a situation where you have to use some dedicated backend, and you have to send some API requests, feel free to try and use our axios-http-client and let us know what are your thoughts about this. Also, if you’re having some issues or problems, feel free to raise an issue on our GitHub repository, here.

I would love to give special thanks to our CTO Vlatko Vlahek who gave us an idea of how to create this client. And also a special thanks to our colleague Marko Mrkonjić who pointed out how to simplify the usage in React applications by providing an example of how to create a reusable hook with this http-client.

Thank you for your the time to read this blog! Feel free to share your thoughts about this topic and drop us an email at hello@prototyp.digital.