Mastering API Data Fetching in React with Next.js
Published: 2024-01-09 11:34:01

---
title: Mastering API Data Fetching in React with Next.js
published: true
date: 2024-01-08 11:17:32 UTC
tags: 
canonical_url: https://danrez.hashnode.dev/mastering-api-data-fetching-in-react-with-nextjs
---

###Introduction to API Data Fetching in React

Data fetching is a crucial aspect of integrating external data sources into React user interfaces. It entails obtaining data from _APIs_, _databases_, or _local storage_ and displaying it within the application to create dynamic and interactive user experiences. Managing API calls is a vital component of React data fetching. **GET** , **POST** , **PUT** , and **DELETE** are employed to interface with APIs and acquire data. **Fetch()** and **axios** enable efficient execution of these queries within React components. React's state management hooks, such as the **useState hook** , allow for handling and updating fetched data within components. For more complex data management or sharing across components, **Redux** or the **Context API** can be utilized. Asynchronous processes are integral to data retrieval. The **useEffect hook** facilitates data fetching after the component has been rendered, which is crucial. _Async/await_ or promises aid in managing asynchronous API calls and responses. Error handling and loading are essential aspects of data fetching. Implementing error handling ensures graceful management of API call errors. Loading indicators enhance the user experience while awaiting data. The ultimate objective is data transformation and rendering. It is common practice to modify API responses before rendering them in React. Conditional rendering and mapping data to _UI_ elements enable the dynamic display of fetched data. Comprehending these React data-fetching concepts is vital for developing robust, interactive, and efficient web applications that seamlessly integrate external data sources, resulting in enhanced user experiences.

**Let's explore the significance of efficient data retrieval and manipulation in dynamic web applications.**

Web applications' dynamism and responsiveness depend on efficient data retrieval and manipulation. Perspective on their significance:

- Dynamic web apps create engaging user experiences in the fast-paced digital world

- Efficient data retrieval and manipulation enable quick access, processing, and display of data from various sources for real-time updates and interaction

- Real-time updates are possible with efficient data retrieval technologies, providing users with the latest data without manual intervention

- **Smoother user experience:** Quick data handling and presentation enhance user interfaces, meeting user expectations for fast loading and seamless interactions

- **Interactive and Personalization:** Dynamic apps personalize experiences using altered data, customizing information, recommendations, and interfaces based on user choices and behaviors.

- **Optimization:** Streamline data retrieval and manipulation to improve app performance, reduce unnecessary data requests, optimize queries, and use caching.

- **Scalability and Flexibility:** Efficient data handling ensures scalability and flexibility as applications evolve, enabling growth without performance loss.

- **Competitive Edge:** Fast-loading, user-friendly apps with efficient data retrieval and manipulation have a competitive advantage, improving user retention and satisfaction.

- **Application Adaptability:** Rapid data retrieval and manipulation allow apps to adapt to changing business needs, providing developers access to relevant data and flexible tools for quick changes.

###Understanding Next.js and API Fetching

To showcase how Next.js streamlines server-side rendering (SSR) or static site generation (SSG) with API querying, we will develop a simple Next.js project outlining the process.

- **Create a new Next.js project:**

Ensure you have **Node.js** installed. Open your terminal and run the following commands:

```
npx create-next-app nextjs-ssr-examplecd nextjs-ssr-example
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1704628856929/33ed4be8-9419-43f4-bee8-48437ab1ffe1.png)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1704628871351/a2712103-8dff-4ba6-b57c-1a21e35629f0.png)

- **Create a mock API route:**

In the pages`/api` directory, create a new file named `data.js` and enter the following date:

```
// pages/api/data.jsconst data = [{ id: 1, name: 'Tesla Model S' }, { id: 2, name: 'Tesla Model 3' }, { id: 3, name: 'Tesla Model X' },]; export default function handler(req, res) { res.status(200).json(data); }
```

- **Create a page that fetches data using SSR.**

In the`pages/index.js` file, implement server-side rendering to fetch and display data. Add the below code:

```
import React from 'react';function Home({ items }) { return ( <div> <h1>Items List</h1> <ul> {items.map((item) => ( <li key={item.id}>{item.name}</li> ))} </ul> </div> );}export async function getServerSideProps() { // Fetch data from the API const res = await fetch('http://localhost:3000/api/data'); const items = await res.json(); return { props: { items, }, };}export default Home;
```

- **Start the development server.**

Run the following command in your terminal:

```
npm run dev
```

Access the server at [http://localhost:3000](http://localhost:3000) in your web browser. The mock API's retrieved items should be visible on the page.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1704629420547/7dac5ca1-ca41-411e-b4b6-f83d9dba2817.png)

This example shows how Next.js streamlines server-side rendering (SSR) by retrieving data from an API. To enable the server to pre-render the page with the fetched data before providing it to the client, the getServerSideProps function retrieves the data and gives it as props to the Home component.

###Basics of API Fetching in React/Next.js

Here we will examine the useState and useEffect hooks for making API queries with fetch.

- Let's start by creating a new component for API fetching

Create a new component named **DataFetchingComponent.js** in the components directory.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1704630695466/c8c558ee-a30d-4b34-b5f4-9a29774465ea.png)

Add the following piece of code inside that file:

```
// components/DataFetchingComponent.jsimport React, { useState, useEffect } from 'react';function DataFetchingComponent() { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { const response = await fetch('http://localhost:3000/api/data'); if (!response.ok) { throw new Error('Network response was not ok.'); } const fetchedData = await response.json(); setData(fetchedData); setLoading(false); } catch (error) { setError(error.message); setLoading(false); } }; fetchData(); }, []); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error}</p>; return ( <div> <h2>Data Fetched from API:</h2> <ul> {data.map((item) => ( <li key={item.id}>{item.name}</li> ))} </ul> </div> );}export default DataFetchingComponent;
```

Let's get to understand the code we've just added:

In this code, we created the **DataFetchingComponent** function that downloads API data and displays it on the page. A simple code explanation:

- **State Management:**

- **Data Fetching using** `useEffect` **:**

- **Conditional rendering based on state:**

- **Exporting the Component:**

Let's now incorporate the component in our Next.js application by updating the **index.js** file with the following code:

```
// pages/index.jsimport React from 'react';import DataFetchingComponent from '../components/DataFetchingComponent';function Home() { return ( <div> <h1>Next.js API Data Fetching Example</h1> <DataFetchingComponent /> </div> );}export default Home;
```

After running the application, we can see we've successfully made an **API** to fetch data using the **fetch()** method

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1704632252290/146b525f-1f27-43b8-86eb-6a85fb4698dc.png)

###Optimizing Data Fetching in Next.js

We use a caching method to decrease the amount of requests sent to external data sources (APIs, databases, etc.) to boost data fetching performance.

**Caching** means temporarily storing data that has already been retrieved and then serving it directly from that cache rather than constantly retrieving the same data.

Here's an example of implementing a simple caching mechanism in a Next.js app using the `swr` library, which provides React hooks for data fetching with built-in caching and revalidation.

Using the [swr](https://swr.vercel.app/) library's React hooks for data fetching with built-in caching and revalidation, we will construct a simple caching scheme.

The first step is to install **swr** in our application using the command below.

```
npm install swr
```

Next, we'll add the following code to our **DataFetchingComponent.js** file.

```
import useSWR from 'swr';export const fetcher = async (url) => { const response = await fetch(url); if (!response.ok) { throw new Error('Failed to fetch data'); } return response.json();};export const useCachedData = (url, options = {}) => { const { data, error } = useSWR(url, fetcher, options); return { data, isLoading: !error && !data, isError: error, };};
```

**Explanation of the code**

*`fetcher` function:*

The `fetcher` function is a utility function used for making API requests using the `fetch` API. It is passed as a parameter to `useSWR` to handle data fetching. Here's what it does:

- Accepts a URL as an argument.

- Uses `fetch` to make an HTTP GET request to the specified URL.

- Checks if the response is successful (status code 200-299).

- If successful, parses the response body as JSON and returns it.

- If unsuccessful, throw an error with the message 'Failed to fetch data'.

 *`useCachedData` hook:*

The `useCachedData` hook is a custom hook that encapsulates the usage of the `useSWR` hook along with the `fetcher` function for data fetching and caching. Here's a breakdown:

- It takes two arguments: `url` (the endpoint URL) and `options` (optional SWR configuration options).

- Uses `useSWR` with the provided URL, `fetcher`, and optional options to perform data fetching and caching.

- Destructures the returned values from `useSWR`: `data` (the fetched data), `error` (any error that occurred during fetching).

- Returns an object with three properties:

The third step is to update our **index.js** file with the following code:

```
import React from 'react';import { useCachedData } from '../components/DataFetchingComponent';const CachedDataPage = () => { const { data, isLoading, isError } = useCachedData( 'https://jsonplaceholder.typicode.com/posts' ); if (isLoading) { return <p>Loading...</p>; } if (isError) { return <p>Error: Failed to fetch data</p>; } return ( <div> <h1>Cached Data Example</h1> {data && ( <ul> {data.map((item) => ( <li key={item.id}>{item.title}</li> ))} </ul> )} </div> );};export default CachedDataPage;
```

The `CachedDataPage` component uses the `useCachedData` hook from the `DataFetchingComponent` to fetch and display data from the **JSONPlaceholder API**. It handles loading and error states and renders a list of titles when the data is successfully fetched. This approach encapsulates data fetching logic, simplifying the component's responsibility to display the fetched data based on the loading and error states managed by the hook.

As shown in the image below, we've successfully used the SWR hook to implement a caching strategy by fetching and displaying data from **https://jsonplaceholder.typicode.com/posts**.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1704708541766/354b1456-32b4-4625-ac2d-93a836930efc.png)

**Caching boosts efficiency through:**

Caching lessens the strain on the server and the amount of traffic travelling via the network by preventing unused queries to the server and instead serving previously cached material.

Data that has been cached is retrieved from local storage or memory and served to users more rapidly, resulting in faster response times.

A better and more responsive user experience is a result of faster loading times made possible by cached data.

Keep in mind that caching solutions should take data refreshment, expiration dates, and cache invalidation into account to guarantee that consumers get current information and optimized speed.

**Best Practices and Tips**

**_Organizing data fetching logic and separating concerns in React/Next.js applications._**

In order to help you organize your data fetching logic, separate your concerns, and handle API data fetching issues efficiently in your React/Next.js applications, I have included some code snippets and recommendations below.

1. **Separate Data Fetching Logic into Functions**

To make data-fetching logic more readable and easier to maintain, group it into independent functions. As an example:

```
// utils/api.jsexport const fetchData = async (url) => { try { const response = await fetch(url); if (!response.ok) { throw new Error('Failed to fetch data'); } return response.json(); } catch (error) { throw new Error('Failed to fetch data'); }};
```

1. **Component-Based Data Fetching**

Particularly when the data is component-specific, it is best to keep data fetching logic inside the corresponding components:

```
import { useEffect, useState } from 'react';import { fetchData } from '../utils/api';const MyComponent = () => { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchComponentData = async () => { try { const result = await fetchData('https://api.example.com/data'); setData(result); } catch (error) { setError('Failed to fetch data'); } finally { setLoading(false); } }; fetchComponentData(); }, []);};
```

1. **Data Fetching Libraries or Hooks**

For managing states, caching, and data retrieval, use libraries like swr or create your own hooks. As an example:

### **Conclusion**

In conclusion, developers can make strong, flexible, and dynamic web apps by learning how to use API data fetching in React with Next.js. Throughout this technical piece, we've looked at the basic ideas and best practices that make data retrieval work well:

- **Framework for Building Dynamic Web Apps:** Next.js is a powerful framework for building dynamic web apps because it combines React components with server-side processing and better ways to get data.

- **Optimized Data Fetching Techniques:** Developers can use the best way for each project by learning about the different data fetching techniques, such as getStaticProps, getServerSideProps, or client-side fetching with libraries like swr.

- **Organized Code Structure:** Using structured and modular code organization for logic that fetches data makes applications easier to manage and more scalable. Using component-based fetching, custom hooks, and utility methods can help you organize and handle data-fetching issues well.

- **Handling Errors and Debugging:** To quickly find, debug, and fix API data fetching problems, you need strong error handling systems that include the right error messages, debugging tools, and monitoring methods.

- **Continuous Improvement:** Having a mindset of continuous improvement means trying out different ways to get data, making API calls work better, and keeping an eye on speed to make applications faster and more reliable over time.