SimpleNext.js

How to fetch external data in Next.js

Cover Image for How to fetch external data in Next.js
Marouane Reda
Marouane Reda

In Next.js, for reasons of performance and SEO ( which is the main reason you will use Next.js instead of plain client-rendered React), Pages are pre-renderd by default : which means the HTML for each page is generated in advance. The browser loads the HTML with minimal JS to be run ( the process is called hydration) Pages in Next.js can be pre-rendered in two ways : Static generation and Server-side Rendering. Static generation is basically generation of the HTML at build time, for example when you run "npm build" before deploying your site. the generated HTML will be reused at each request. Server-side Rendering is generation of the HTML on each request.

the question is : when to use each pre-rendering strategy; and how to implement both of them?

The short answer is to use Static generation unless Server-side generation is absolutely necessary. The static generation is obviously more performant , as the HTML does not to be regenerated at each request. But, as the HTML page is generated at build time, some cases make the use of Server-side rendering mandatory.

Static generation : getStaticProps and getStaticPaths

if our Page does not require fetching of external data, the page is automatically generated in Static generation mode :

function Nodata() {
  return <div>No external data needed</div>
}

export default Nodata

if we need to fetch external data to use in our page ( from an external API, database, …), getStaticProps comes into play.

Let’s take an example of a blog that needs to fetch posts from an API

// TODO: Need to fetch `posts` 
//(by calling some API endpoint)
//  before this page can be pre-rendered.
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

export default Blog

To fetch this data on pre-render, Next.js allows you to export an async function called getStaticProps from the same file. This function gets called at build time and lets you pass fetched data to the page's props on pre-render.

function Blog({ posts }) {
  // Render posts...
}

// This function gets called at build time
export async function getStaticProps() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()

  // By returning { props: { posts } }, 
  // the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  }
}

export default Blog

getStaticProps can only be used in Pages ( under Pages directory )

the obvious question here is : as the HTML pages are generated at build time, what about if the data has changed between the build time and the request time? ( for example new posts have been added)

here comes the Incremental Static generation into play. let’s see the code :

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

// This function gets called at 
// build time on server-side.
// It may be called again, on a 
// serverless function, if
// revalidation is enabled and a 
//new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js will attempt to
    // re-generate the page:
    // - When a request comes in
    // - At most once every second
    revalidate: 1, // In seconds
  }
}

export default Blog

by adding the revalidate: 1 to the return statement of getStaticProps, Next.js will regenerate the page at most once every second , and whenever a request comes in. That ensures that our list of posts is almost up to date to the second. This process is called Incremental Static generation.

If we have a page that use dynamic routing , we need to define a list of paths to be statically generated at build time using getStaticPaths. this will be covered in another post , but in short we use a similar strategy as revalidate to ensure our list is almost up to date.

Server-side rendering

the Server-side rendering uses getServerSideProps in a same fashion as getStaticProps is used in Static generation :

function Page({ data }) {
  // Render data...
}

// This gets called on every request
export async function getServerSideProps() {
  // Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()

  // Pass data to the page via props
  return { props: { data } }
}

export default Page

getServerSideProps can only be used in Pages ( under Pages directory ) Here, the data is fetched as each request, so it is less performant then Static generation when HTML is already available.

But when the page cannot be pre-rendered ahead of a user’s request, Server-side rendering becomes the only solution, for example for data that changes constantly, or for user-related data.