Context I've been developing a web application using Next.js and a custom Django Python backend, but I am struggling to find an efficient approach for making API requests from my frontend to the backend. My main goal is to centralize the logic for fetching data while also implementing type safety for request bodies and responses.
Current Strategy
const api = {
get: async function (url: string): Promise<any> {
console.log("get", url);
return new Promise((resolve, reject) => {
fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}${url}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
cache: "no-store",
})
.then((response) => response.json())
.then((json) => {
console.log("Response:", json);
resolve(json);
})
.catch((error) => {
reject(error);
});
});
},
post: async function (url: string, data: any): Promise<any> {
console.log("post", url, data);
return new Promise((resolve, reject) => {
fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}${url}`, {
method: "POST",
body: JSON.stringify(data),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
})
.then((response) => response.json())
.then((json) => {
console.log("Response:", json);
resolve(json);
})
.catch((error) => {
reject(error);
});
});
},
}
While this approach centralizes the fetch logic for GET and POST requests, it lacks type safety as both methods accept and return data of type 'any'. The repetition of code for future methods such as PATCH and DELETE is also a concern.
Alternative Approach
export async function getProfile(id: string): Promise<Profile> {
try {
const response = (await api.get(getProfileRoute(id))) as Profile;
return response;
} catch (error) {
console.log("Could not fetch profile...", error);
}
}
This example offers more type safety by specifying the return type of the function and accepting an argument that can be typed to ensure correct data for a POST request.
Route Handlers After exploring Next.js documentation on route handlers route handlers, I found similar challenges in maintaining fetch logic without sacrificing type safety. Creating separate files for each HTTP method seems inefficient.
Question How can I implement a cleaner way to interact with my backend API from Next.js? The solution should:
- Make fetch logic reusable across different HTTP methods without constant repetition.
- Ensure proper typing for data sent in POST requests and received responses.