If you’ve been working as a frontend developer for a while, you’ve probably written your fair share of API calls. At first, using Axios feels super straightforward. But as the project grows, API calls start getting messy, repetitive, and hard to manage.
Let’s be real—when a new dev joins the team, they’ll probably struggle to figure out what’s going on (just like I did during my first internship!). So how do we keep things clean, readable, and scalable? That’s where the Builder Pattern comes in.
The Problem with Basic Axios Calls
Here’s a typical API call using Axios:
import axios from "axios";
async function fetchUser(userId) {
try {
const response = await axios.get(`https://api.example.com/users/${userId}`, {
headers: {
"Content-Type": "application/json",
Authorization: "Bearer someToken",
},
params: { includePosts: true },
});
console.log(response.data);
} catch (error) {
console.error("API Error:", error);
}
}
What’s Wrong With This?
- Too much repetition – If you need the same headers or params elsewhere, you’ll have to copy-paste them.
- Hard to read – New developers might take time to understand the request structure.
- Not scalable – As the number of API calls increases, maintaining consistency across them gets painful.
The Fix: Using the Builder Pattern
The Builder Pattern helps us structure API calls in a reusable and readable way.
Step 1: Create a Request Builder
Here’s how we can wrap Axios calls into a reusable class:
import axios from "axios";
class ApiRequestBuilder {
constructor() {
this.config = {
method: "get",
url: "",
headers: {},
params: {},
data: null,
};
}
setMethod(method) {
this.config.method = method;
return this;
}
setUrl(url) {
this.config.url = url;
return this;
}
setHeaders(headers) {
this.config.headers = { ...this.config.headers, ...headers };
return this;
}
setParams(params) {
this.config.params = params;
return this;
}
setData(data) {
this.config.data = data;
return this;
}
async send() {
try {
const response = await axios(this.config);
return response.data;
} catch (error) {
console.error("API Error:", error);
throw error;
}
}
}
export default ApiRequestBuilder;
Step 2: Use the Builder to Make API Calls
Now, instead of writing messy API calls, we can use the ApiRequestBuilder
like this:
Fetching a User (GET Request)
import ApiRequestBuilder from "./ApiRequestBuilder";
async function fetchUser(userId) {
try {
const user = await new ApiRequestBuilder()
.setMethod("get")
.setUrl(`https://api.example.com/users/${userId}`)
.setHeaders({ "Content-Type": "application/json", Authorization: "Bearer someToken" })
.setParams({ includePosts: true })
.send();
console.log("User Data:", user);
} catch (error) {
console.error("Error fetching user:", error);
}
}
Creating a User (POST Request)
async function createUser(userData) {
try {
const newUser = await new ApiRequestBuilder()
.setMethod("post")
.setUrl("https://api.example.com/users")
.setHeaders({ "Content-Type": "application/json" })
.setData(userData)
.send();
console.log("User Created:", newUser);
} catch (error) {
console.error("Error creating user:", error);
}
}
Why Use the Builder Pattern for Axios?
Cleaner Code – No more long, repetitive API calls.
Reusable & Scalable – Define headers, params, and methods once and reuse them anywhere.
Easier to Maintain – Changing API structure? Just update the builder class!
Better for Teams – New devs can understand API calls faster.
If you’ve ever had to hunt down inconsistent API calls across a project, this pattern will save you a ton of headaches. 😅
Wrapping Up
The Builder Pattern is an awesome way to clean up Axios API calls in frontend projects. It improves readability, maintainability, and scalability, making your life (and your team’s) much easier.
If you’re tired of messy API calls, give this pattern a shot! 🚀