Hey there! I'm Chhakuli, working as a Front-End developer for a while now. Today, I want to share with you how you can make your app a lot better by changing how you handle API calls. Don't worry if you're new to this stuff - I'll explain everything in simple terms.
As developers, we constantly deal with APIs to fetch or send data, and in larger applications, API management can easily become chaotic. If you’re working with a framework like Next.js, you already have a robust framework that supports both client-side rendering (CSR) and server-side rendering (SSR). This dual capability makes Next.js
a perfect environment to centralize API calls, offering flexibility and simplicity.
The Common Problem
Picture this scenario: You have a large Next.js app. It's functional, but there's a recurring issue. Every time data needs to be fetched from the server (that's what an API call does), you find yourself writing repetitive code. It's like cooking dinner and having to get out all the pots and pans each time you want to make a meal. It becomes a real pain!
Here's what the code often looks like:
const getData = async () => {
try {
const response = await fetch('https://my-api.com/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
const data = await response.json();
return data;
} catch (error) {
console.error('Oops, something went wrong:', error);
}
};
This code might be scattered all over the app. It's messy and hard to keep track of. Plus, if you want to change something (like how errors are handled), you'd have to change it in multiple places. Not fun!
My Light Bulb Moment
One day, in a code review session, I got feedback from my senior. It was kind of like making a special kitchen helper who always gets your pots and pans ready for you.
The Solution: Centralizing API Calls
The solution to this problem is to create an "HTTP service." Don't let the fancy name scare you - it's just a file where all API-related code is centralized. Here's how to implement it:
Create a new file called
httpService.ts
. This file will be in charge of all API calls.In this file, create a main function called
request
. This function knows how to talk to the server and get the required data.Add some helper functions like
get
,post
,patch
, anddel
(usedel
instead ofdelete
becausedelete
is a reserved word in JavaScript).
Here's what the httpService.ts
might look like:
const API_BASE_URL = 'https://my-api.com';
const defaultHeaders = {
'Content-Type': 'application/json',
};
const request = async (method, url, payload, customHeaders) => {
const headers = { ...defaultHeaders, ...customHeaders };
let body;
if (payload) {
body = JSON.stringify(payload);
}
try {
const response = await fetch(`${API_BASE_URL}${url}`, {
method,
headers,
body,
});
const data = await response.json();
if (!response.ok) {
throw { status: response.status, data };
}
return data;
} catch (error) {
console.error(`Oops, something went wrong with ${url}:`, error);
throw error;
}
};
const get = (url, customHeaders) => request('GET', url, null, customHeaders);
const post = (url, payload, customHeaders) => request('POST', url, payload, customHeaders);
const patch = (url, payload, customHeaders) => request('PATCH', url, payload, customHeaders);
const del = (url, customHeaders) => request('DELETE', url, null, customHeaders);
export { get, post, patch, del };
It might look like a lot, but the cool thing is, you only need to write this once, and then you can use it throughout your app.
Using the New HTTP Service
Now, when you want to get data from your server, you can do it like this:
import { get } from '../httpService';
const getUserData = async (userId) => {
try {
const data = await get(`/users/${userId}`);
return data;
} catch (error) {
console.error('Could not get user data:', error);
}
};
See how much simpler that is? You don't have to worry about all the details of making an API call anymore. The httpService
takes care of all that for you.
Why This Change is Awesome
Let me tell you why this new way of doing things is so great:
Less Repetition: I don't have to write the same API code over and over again. It's like having a recipe that I can use for lots of different meals.
Easier to Fix Errors: If something goes wrong with my API calls, I know exactly where to look. It's all in one place!
Simpler to Make Changes: If I need to change how my API calls work (like adding some security stuff), I can do it in one place, and it updates everywhere in my app.
Cleaner Code: My components (the building blocks of my app) look much neater now. They're not cluttered with all that API code.
Easier to Test: When I want to make sure my app is working correctly, it's much easier to test my API calls now.
Better Security: I can add things like authentication tokens to all my API calls really easily now.
Room to Grow: As my app gets bigger, it's super easy to add new types of API calls.
Real-Life Example: Login System
Let's look at a real example of how this helps. Say you're building a login system for your app. Before, you might have done something like this
const login = async (username, password) => {
try {
const response = await fetch('https://my-api.com/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
});
const data = await response.json();
if (!response.ok) {
throw new Error('Login failed');
}
return data;
} catch (error) {
console.error('Login error:', error);
}
};
Now, with my new httpService
, it looks like this:
import { post } from '../httpService';
const login = async (username, password) => {
try {
const data = await post('/login', { username, password });
return data;
} catch (error) {
console.error('Login error:', error);
}
};
Much cleaner, right? If you need to change how your login works (like adding a new security check), you can do that in the httpService
, and it will automatically apply to all your API calls.
The Journey of Implementing This Change
Implementing this change in an existing app isn't always easy. Here's a general process you might follow:
Planning: Spend time planning how the
httpService
should work. Make sure it can handle all the different types of API calls you might need.Creating the Service: Writing the
httpService
itself takes some time. Consider error handling, how to make it flexible for different types of requests, and how to make it easy to use.Updating Your Code: This is the big job. Go through your entire app and replace all your old API calls with your new
httpService
calls. It takes a while, but it's worth it.Learning and Adjusting: As you start using the new system, you'll likely find ways to improve it. It's an ongoing process of learning and refining.
Lessons I Learned
Going through this process can teach you a lot. Here are some key takeaways:
Planning Pays Off: Taking the time to plan your approach before you start coding can save you a lot of headaches later.
Consistency is Key: Having a standard way of doing API calls across your app makes everything easier to understand and maintain.
Flexibility Matters: Make sure the
httpService
is flexible enough to handle different types of API calls. This will be super helpful as your app grows.It's Okay to Iterate: You might not get everything perfect the first time, and that's okay. Keep improving the
httpService
as you use it more.
Wrapping Up
Centralizing API calls in a Next.js app can significantly improve code quality and maintainability. It's a big change, but it makes life as a developer much easier. Your code becomes cleaner, it's easier to add new features, and when something goes wrong, it's much easier to figure out why.
If you're working on a Next.js app (or any React app, really) and you're feeling overwhelmed by API calls everywhere, give this approach a try. It might take some work upfront, but it's worth it in the long run.
Remember, good code isn't just about making things work - it's about making things work well and making your life as a developer easier. So don't be afraid to step back, look at the big picture, and make changes that will help you in the long run.
Happy coding, everyone! And if you try this out in your own projects, share your experience. Did it help? Did you do things differently? Let others know! Don't forget to share this blog with your peers to spread the knowledge and inspire others!
We at CreoWis believe in sharing knowledge publicly to help the developer community grow. Let’s collaborate, ideate, and craft passion to deliver awe-inspiring product experiences to the world.
Let's connect:
This article is crafted by Chhakuli Zingare, a passionate developer at CreoWis. You can reach out to her on X/Twitter, LinkedIn, and follow her work on the GitHub.
Top comments (10)
Insightful read. Learnt something new which can be used in some of my projects. Thanks for sharing :)
Thank you, Anand! 😊 I'm glad you found it useful and can apply it to your projects. Best of luck with your implementations!
Useful information. I have tried same approach before, but this is much better than that. Got to learn something, thanks for sharing!
Thanks so much, Pranita! I’m happy to hear this approach works better for you. It's great that you could learn something new. 😊
Amazing concepts that make developers' lives easier. Thanks for sharing!
Thanks, Syket! 🙌.
It certainly helps in getting all the calls at one place, thus improves in code maintainability.
Thanks a lot, Tapas! 😊 Yes, centralizing the calls definitely helps with maintainability.
Great blog! Thanks for sharing.
Thank you so much, Prachi! 😊 I’m really happy you enjoyed the blog. Thanks for the support!