Data Fetching in Next.js

Fetch

By Chandrashekhar Fakirpure

Updated on Jan 26, 2024

In this tutorial, we will learn about data fetching in Next.js https://nextjs.org/docs/. Data fetching is a core part of any application. Next.js extends the native fetch https://developer.mozilla.org/docs/Web/API/Fetch_API. We can configure caching and revalidating behavior for each request. 

Data fetching allows us to get data from the database and making it available to the application. We can create a single file for multiple data requests or fetching data where it's needed. If the same data being use for the user, we can use fetch in components that needs the data without worrying about the performance implications of making multiple requests for same data. Fetch requests are automatically memorized.

Simple data fetching

For example:

export async function getData() {
    const res = await fetch('https://api.example.com/get_data/')
    if (!res.ok) {
        return {
            props: {
                data: [],
            },
        };
    }
    const data = await res.json()
    return data
}

Let's understand the code. 

To use fetching, we need to make the function async because it make sure that the function returns a promise and wraps non-promise in it. 

To make a fetch() request, we need following syntax. We have added await because it may take some time to get the data, so it keep the connection open and waiting for data. Once we get the response, store in variable res.

const res = await fetch('https://api.example.com/get_data/')

Next, we have added *if condition* to check if response of the fetch request is ok or we have received error. If the response is not ok we'll return empty prop object. Here we can throw and error like: throw new Error('Failed to fetch data').

If get response, we store in *data* variable and return it.

Fetch data in same components

We can use *fetch()* in the same component where data is needed. This also applies to layouts, because it's not possible to pass data between a parent layout and its children.

async function getData() {
    const res = await fetch('https://api.example.com/get_data/')
    if (!res.ok) {
        return {
            props: {
                data: [],
            },
        };
    }
    const data = await res.json()
    return data
}

export default async function Page() {
  const data = await getData()
 
  return <main></main>
}

Advanced data fetching

We can add headers and Authorization token in fetch().

export async function getData() {
    const res = await fetch('https://api.example.com/get_data/', {
        headers: {
            Authorization: `Bearer a7e13ea3740b933496d88755ff341bfb824805a6`,
            'Content-type': 'application/json',
        }
    })
    if (!res.ok) {
        return {
            props: {
                data: [],
            },
        };
    }
    const data = await res.json()
    return data
}

This is the example code. You should not put authorization token and API URL in code. Use environmental variable.

Revalidating Data

With revalidating data, we can purge the cache and re-fetch the data from the source or database to show the latest information. There are two types of revalidation in Next.js. 

  • Time-based revalidation
  • On-demand revalidation

Time-based revalidation

We can get the data after a certain amount of time interval. We can use next.revalidate option to fetch the data. The fetched data will store in data cache. Any requests that are called within the specified timeframe will return the cached data. We can set the revalidation for each request differently. 

The fresh data or information get fetch and stored in cache within the mentioned timeframe. It will help us to reduce the number of request to the API source. Next.js will trigger a revalidation of the data in the background.

For example:

export async function getData() {
    const res = await fetch('https://api.example.com/getdata/', {
        next: { revalidate: 3600 }
    })
    if (!res.ok) {
        return {
            props: {
                data: [],
            },
        };
    }
    const data = await res.json()
    return data
}

Here we are mentioned 3600. It will revalidate at most every hour

On-demand revalidation

On-demand revalidation is different from time-based revalidation. The data keeps in the cache until the fresh data is fetched. It will trigger the appropriate cache entries will be purged from the cache. Here we can revalidate the fetch on-demand by path(revalidatePath) or by cache tag (revalidateTag).  We can use one or more to tag cache entries then we can call *revalidateTag* to revalidate all entries associated with that tag.

For example: [app.page.tsx]

export async function getData() {
    const res = await fetch('https://api.example.com/getdata/', {
        next: { tags: ['collection'] }
    })
    if (!res.ok) {
        return {
            props: {
                data: [],
            },
        };
    }
    const data = await res.json()
    return data
}

For the revalidation this fetch, we can call the tag named collection in server action [app/action.ts]:

'use server'
 
import { revalidateTag } from 'next/cache'
 
export default async function action() {
  revalidateTag('collection')
}

For more information visit official on-demand revalidation documentation.

Disable fetch data caching 

If we want to disable data fetching cache, we can do it by following way:

  • The cache: 'no-store' is added to fetch requests.
  • The revalidate: 0 option is added to individual fetch requests.
  • The fetch request is inside a Router Handler that uses the POST method.
  • The fetch request comes after the usage of headers or cookies.
  • The const dynamic = 'force-dynamic' route segment option is used.
  • The fetchCache route segment option is configured to skip cache by default.
  • The fetch request uses Authorization or Cookie headers and there's an uncached request above it in the component tree.

Note: Above contents are referred from official Next.js documents website.

We have completed the data fetching in Next.js chapter.