DEV Community

Cover image for Data Fetching in NextJS
Minh Nguyen
Minh Nguyen

Posted on

Data Fetching in NextJS

Next.js provides a variety of methods and techniques for data fetching, enabling you to select the most suitable approach based on your application's requirements. These data fetching techniques play a crucial role in enhancing the performance and overall user experience of your Next.js applications.

When it comes to data fetching, choosing the right method is pivotal. The choice may vary depending on factors such as data freshness, security concerns, and performance expectations.

One of the recommended approaches is server-side data fetching, which offers several benefits:

🗄️ Directly access backend data resources, such as databases.

🛡️ Enhance security by preventing sensitive information like API keys from being exposed to clients.

đźš„ Fetch and render data within the same environment, thereby reducing network round-trips between the client and server.

đź’Ş Depending on your situation, data fetching can occur closer to the data source, minimizing latency and consequently improving performance.


In this post, we'll delve into these recommended data fetching patterns by building a captivating quote application that showcases the power and advantages of these approaches.

App Overview

Welcome to inspiring Quote App, inspired by freeCodeCamp’s Quote Machine challenge. Get ready to embark on a journey of motivation and creativity as I present you with an array of inspirational quotes accompanied by stunning images from Unsplash.

We will explore a variety of data fetching patterns. At the end, I'll provide you with valuable speed insights, showcasing the optimization techniques that contribute to the app's snappy performance.

Technologies Used

Data Fetching Patterns in NextJS

I utilize Next.js's built-in fetch, an extension of the equivalent Web API, which automatically memoizes fetch requests.

Here's a brief example:

async function getData() {
  const res = await fetch('https://api.example.com/...')

  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}
Enter fullscreen mode Exit fullscreen mode

Sequential Data Fetching

Sequential data fetching in Next.js refers to a pattern where you fetch data one after the other, waiting for each fetch to finish before starting the next one.

Sequential data fetching can be useful in scenarios where the results of one data request are dependent on the data fetched in previous requests, or when you want to ensure a specific order of data fetching.

// app/patterns/sequential/page.tsx
export default async function SequentialFetching() {
    const photo = await getPhoto()
    const quote = await getQuote()

    return (
        <>
            <BackgroundImage ... />
            <QuoteCard ... />
        </>
    );
}

Enter fullscreen mode Exit fullscreen mode

Keep in mind that sequential data fetching might also introduce delays if each request takes a considerable amount of time. In cases where data requests are independent and can be fetched concurrently, you might want to consider parallel data fetching for improved performance. That leads us to the next pattern.

Parallel Data Fetching

Parallel data fetching in Next.js is a pattern where multiple data requests are made simultaneously, without waiting for one request to complete before starting another. This approach can lead to improved performance and reduced loading times, especially when dealing with independent data sources or APIs. Parallel fetching takes advantage of the fact that modern browsers can handle multiple network requests concurrently.

To fetch data in parallel, you can eagerly initiate requests by defining them outside the components that use the data, then calling them from other component .i.e inside the parent component. This saves time by initiating both requests in parallel, however, the user won't see the rendered result until all promises are resolved.

// app/patterns/parallel/page.tsx
export default async function ParallelFetching() {
    const [photo, quote] = await Promise.all([getPhoto(), getQuote()])

    return (
        <>
            <BackgroundImage ... />
            <QuoteCard ... />
        </>
    );
}

Enter fullscreen mode Exit fullscreen mode

Preloading Data

The preloading data pattern in Next.js involves fetching data in advance to further optimize parallel data fetching and prevent waterfalls. This technique aims to improve the user experience by reducing the perceived loading time. By preloading data, you can ensure that the necessary information is readily available.

// app/patterns/preload/page.tsx
export default async function Preloading() {
    getPhoto()
    getQuote()

    return (
        <>
            <BackgroundImageContainer />

            <Suspense fallback={<QuoteCardSkeleton />}>
                <QuoteCardContainer />
            </Suspense>
        </>
    );
}

const BackgroundImageContainer = async () => {
    const photo = await getPhoto()

    return (
        <BackgroundImage ... />
    )
}

const QuoteCardContainer = async () => {
    const quote = await getQuote()

    return (
        <QuoteCard ... />
    )
}
Enter fullscreen mode Exit fullscreen mode

Metrics

Overall Metrics from Vercel

Image description

Metrics for each page

Image description

It's not unexpected that the Sequential pattern performs less optimally compared to the other approaches, but it's interesting to see that the Parallel pattern outperforms the Preload pattern. To gain a deeper understanding, I conducted another test by introducing a synthetic delay to replicate real-world conditions.


function delay(n: number) {
    return new Promise(r => setTimeout(r, n))
}

export async function getPhoto(): Promise<Photo> {
    await delay(3000)

    const res = await fetch(...)

    ...
}

export async function getQuote(): Promise<Quote> {
    await delay(3000)

    const res = await fetch(...)

    ...
}

Enter fullscreen mode Exit fullscreen mode

And results are still the same - Parallel pattern still outperforms the Preload pattern :)

Parallel pattern with synthetic delay

Preload pattern with synthetic delay


That's it for today.

As always, I'm looking forward to hearing your thoughts in the comments section. Feel free to share your insights and experiences with these different data fetching patterns.

I'm aware that there are other fetching methods like SSG and client-side fetching using your preferred library, such as react-query. If you're intrigued by this topic, and if you'd like me to explore it further in a future blog post, please let me know in the comments. Your interest and feedback are highly valuable, and I'll be more than happy to delve deeper into the subject.

I hope you find this useful. If you have any favourite quotes, I'd love to hear them.

Top comments (1)

Collapse
 
seanicus profile image
sean

Thank you for the helpful post!