Streamlining APIs using an Axios Client
This article discusses a wrapper class for Axios to simplify and standardize API requests in React.
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.
Axios is straightforward to use, but creating API calls can become messy if you repeat configuration across multiple files.
We encountered this issue in one of our projects, where API calls had become complicated and bloated our code. To address this, we created a class-based wrapper around Axios. This centralized the configuration to a single file, cleaning up our code. It also standardized how API requests are made, improving consistency.
By the end, we had developed a reusable solution that can simplify API calls for any project, regardless of framework. Keep reading to learn more about our Axios wrapper and how it streamlined API requests.
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 simplified example, it’s fairly easy to use. Only, if we were to use it in a real-world application, things could get a 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 a user visits.
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
In one of the applications we were developing, API calls had become complicated and bloated our code due to the initial setup. We noticed this issue and wanted to refactor the code. We developed a class-based solution for making API calls that can now be reused across most projects, regardless of the framework.
This led us to create axios-http-client
which is also straightforward to implement.
You simply define a shareable class, add any necessary configuration, and then you can reuse it throughout your project. For 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 repo or our npm repo. In this documentation, you can also find a simplified way to use it in a React application as a hook.
There were two main reasons we created axios-http-client:
- We wanted to simplify how API requests were created in order to clean up the code and make it easier to understand.
- We aimed to make API requests more consistent and less prone to errors by standardizing the process across the project.
Axios interceptors were left 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
When setting up API calls, it's easy for the code to become messy and disorganized if the process is not properly standardized. While this may not be a major issue for small applications, it can significantly complicate development on larger projects.
Our axios-http-client aims to address this by providing a simple and reusable way to make API requests. Feel free to try it out on your next project and let us know your thoughts. If you have any other questions or run into issues, please check our GitHub repository.
I'd like to thank our CTO Vlatko Vlahek, who initially suggested developing a client to simplify API calls. Thanks also to colleague Marko Mrkonjić for demonstrating how to create a reusable hook for making requests in React applications. We appreciate the contributions that helped shape this library.
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.