Part 57: Refactoring API Calls in Next.js for Enhanced Code Reusability

[App] Headless CMS

[App] Headless CMS

In the world of web development, code duplication is a common issue that can lead to maintenance headaches and potential bugs. In our Next.js application, we recently encountered such duplication between two functions: getReview and getReviews. Both functions were fetching data from Strapi, but they shared a significant amount of code. In this blog post, we'll walk you through the process of refactoring these functions to eliminate redundancy and improve the overall code structure.

Identifying the Duplication

The core of the duplication issue lay in how both getReview and getReviews were handling HTTP requests to fetch data from the Strapi CMS. They both constructed URLs, made fetch requests, and processed JSON responses in a similar manner.

Step 1: Extracting Common Fetch Logic

To reduce duplication, we started by creating a new internal function called fetchReviews. This function encapsulates the shared logic for making API requests and returns the JSON response. By doing this, we can reuse this function in both getReview and getReviews.

Here's how the fetchReviews function looks:

//lib/reviewes.js
async function fetchReviews(parameters) {
  const url = `${CMS_URL}/api/reviews?`
    + qs.stringify(parameters, { encodeValuesOnly: true });
  console.log('[fetchReviews]:', url);
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`CMS returned ${response.status} for ${url}`);
  }
  return await response.json();
}

Key Features of fetchReviews:

  • Parameters as Arguments: We pass API parameters as arguments, allowing for flexible requests with different options.

  • Error Handling: We check if the response is "ok" and throw an error with useful information if it's not. This centralized error handling eliminates the need to duplicate error checks.

Step 2: Refactor getReviews

With fetchReviews in place, we refactored getReviews to use this new function. The refactored getReviews calls fetchReviews, passing the necessary parameters and mapping the response data using a new function, toReview.

export async function getReviews() {
  const { data } = await fetchReviews({
    fields: ['slug', 'title', 'subtitle', 'publishedAt'],
    populate: { image: { fields: ['url'] } },
    sort: ['publishedAt:desc'],
    pagination: { pageSize: 6 },
  });
  return data.map(toReview);
}

Step 3: Refactor getReview

Similarly, we updated getReview to leverage fetchReviews. This function also uses toReview but adds the body field separately due to its unique requirements.

export async function getReview(slug) {
  const { data } = await fetchReviews({
    filters: { slug: { $eq: slug } },
    fields: ['slug', 'title', 'subtitle', 'publishedAt', 'body'],
    populate: { image: { fields: ['url'] } },
    pagination: { pageSize: 1, withCount: false },
  });
  const item = data[0];
  return {
    ...toReview(item),
   body: marked(item.attributes.body,{headerIds: false, mangle: false}),
  };
}

Step 4: Converting CMS Data to Custom Objects

To further streamline the data conversion process, we introduced the toReview function. This function takes an item from the CMS and extracts the necessary fields to create our custom review objects.

function toReview(item) {
  const { attributes } = item;
  return {
    slug: attributes.slug,
    title: attributes.title,
    date: attributes.publishedAt.slice(0, 'yyyy-mm-dd'.length),
    image: CMS_URL + attributes.image.data.attributes.url,
  };
}

Benefits of Refactoring

By refactoring the code, we've achieved several key benefits:

  • Reduced Duplication: The common logic is now centralized, making the codebase cleaner and easier to maintain.

  • Enhanced Flexibility: Changing the API request logic can be done in one place, reducing the risk of inconsistencies.

  • Improved Error Handling: Centralized error management ensures consistent and informative error messages.

Conclusion

Refactoring is a crucial practice in software development that enhances code quality and maintainability. By extracting common logic into reusable functions, we've not only cleaned up our code but also prepared it for future enhancements. This refactoring lays a solid foundation for implementing additional API requests, such as the upcoming reimplementation of the getSlugs function.

Stay tuned for more updates as we continue to refine our Next.js application and explore new ways to improve its performance and usability.

Last updated