Part 50: Refactoring and Enhanced Error Handling in Next.js
[Pages] Data Fetching

When building web applications, efficient error handling is crucial to ensure a robust user experience. Recently, we tackled an issue in our Next.js application where returning a 404 for any error could lead to unintended consequences, such as serving a "not found" page for valid products simply because our app couldn't connect to the CMS. In this post, we'll explore how we can refactor our code for better error handling and maintainability.
The Problem with Generic 404 Errors
Previously, our ProductPage component returned a 404 error for any problem encountered while fetching product data. This was problematic because it didn't distinguish between a non-existent product and a temporary issue like a CMS outage. To address this, we need to refine how we handle errors in our getProduct function, ensuring we only return a 404 when a product truly doesn't exist.
Step-by-Step Refactoring
Creating a Common fetchJson Function
fetchJson FunctionTo eliminate repeated code in our getProduct and getProducts functions, we first refactored them to use a shared function, fetchJson, responsible for making HTTP requests and handling responses.
// lib/api.js
export async function fetchJson(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`request failed: ${response.status}`);
}
return await response.json();
}Modifying Product Functions to Use fetchJson
fetchJsonWith fetchJson handling the fetch logic, we updated our product-related functions to utilize it, simplifying our codebase.
// lib/products.js
import { fetchJson } from './api';
const CMS_URL = 'http://localhost:1337';
export async function getProduct(id) {
const product = await fetchJson(`${CMS_URL}/products/${id}`);
return stripProduct(product);
}
export async function getProducts() {
const products = await fetchJson(`${CMS_URL}/products`);
return products.map(stripProduct);
}
function stripProduct(product) {
return {
id: product.id,
title: product.title,
description: product.description,
};
}Explanation
fetchJsonFunction: Centralizes the logic for making HTTP requests and checking for successful responses, throwing an error if the request fails.Refactoring: By replacing individual fetch calls with
fetchJson, we avoid code duplication and ensure consistent error handling across different functions.
Centralizing API Calls
To further enhance our code, we extracted the fetchJson function into a separate api.js module. This separation allows for a clean, reusable function that can be used for any API calls, not just those related to products.
Using a Centralized CMS URL
We introduced a CMS_URL constant to avoid hardcoding the base URL in multiple places, improving maintainability and reducing the risk of errors if the URL changes.
Benefits of Refactoring
Cleaner Code: By removing repeated code and centralizing logic, our codebase is easier to read and maintain.
Improved Error Handling: With a dedicated function for API requests, we can implement consistent and comprehensive error handling in one place.
Flexibility: The
fetchJsonfunction can now be reused across different modules, promoting code reuse and reducing redundancy.
Conclusion
Refactoring is not just about making code cleaner; it's about making it more robust and easier to maintain. By centralizing our HTTP request logic and refining our error handling strategy, we've set the stage for a more resilient application. In future steps, we'll continue to enhance our error handling to ensure that our application behaves predictably even in the face of unexpected challenges.
Last updated