#02 – Data Fetching – Learn Next.js

Next.js provides three functions to fetch data for pre-rendered pages:

getStaticProps():

  1. Used for static generation (pre-rendering technique)
  2. Fetches data at build-time
  3. When an async function getStaticProps() is called from the page component, the next.js will pre-render this page at build time using the props returned by that function (see Examples)
  4. The context parameter used as an argument of getStaticProps() function is an object containing following parameters (see details here):
    1. params
    2. preview
    3. previewData
    4. locale
    5. locals
    6. defaultLocale
  5. getStaticProps() function returns an object with following parameters (see details here):
    1. props (required)
    2. revalidate
    3. notFound
    4. redirect
  6. Imports used inside the getStaticProps() function are not bundled for the client side
  7. Server side code can be written inside the getStaticProps() function e.g. to read files on the server or to communicate with the database
  8. API routes defined inside the next.js app should not be called inside getStaticProps() function using fetch() instead import the API route and call its function
  9. Incremental Static Regeneration
    1. Used to update the statically served page in the background after some interval defined as revalidate parameter
  10. Query parameters are not available inside the getStaticProps() function because it runs on the build-time instead of request-time
  11. Server-side code (e.g. database queries) can be written securely inside this function because this function only runs on the server side and is not included in client-side bundle
  12. This function saves the returned data inside a JSON file along with the HTML file which can then be used for future page requests using next/link or next/router
  13. This means that in client-side page transitions, the getStaticProps() function is not called instead the data saved in the JSON file is passed to the component
  14. getStaticProps() function can only be exported from pages
  15. In development (npm run dev), getStaticProps() function is called on every request

Example (JavaScript):

//https://nextjs.org/docs/basic-features/data-fetching
// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries. See the "Technical details" section.
export async function getStaticProps(context) {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  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,
    },
    // 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

Example (TypeScript):

import { InferGetStaticPropsType } from 'next'

type Post = {
  author: string
  content: string
}

export const getStaticProps = async (context) => {
  const res = await fetch('https://.../posts')
  const posts: Post[] = 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
  }
}

function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
  // will resolve posts to type Post[]
}

export default Blog

getStaticPaths():

  • Used for pre-rendering of paths (or dynamic routes e.g. /pages/posts/[id].js) at build-time
  • This function need to return paths object (required)
  • The paths returned using paths object are pre-rendered at build-time
  • Fallback key is also required and can have either true or false values
  • fallback: false means that the pages other than the returned from paths object will show 404
  • fallback: false is useful if there are small number of pages to be statically generated otherwise the build time will be increased
  • fallback: true can be used if you want to pre-render some pages at build-time then load the other pages when they are requested using the Fallback pages (see example – fallback: true)

fallback: true is useful if your app has a very large number of static pages that depend on data (think: a very large e-commerce site). You want to pre-render all product pages, but then your builds would take forever.

Instead, you may statically generate a small subset of pages and use fallback: true for the rest. When someone requests a page that’s not generated yet, the user will see the page with a loading indicator. Shortly after, getStaticProps finishes and the page will be rendered with the requested data. From now on, everyone who requests the same page will get the statically pre-rendered page.

This ensures that users always have a fast experience while preserving fast builds and the benefits of Static Generation.

fallback: true will not update generated pages, for that take a look at Incremental Static Regeneration.

https://nextjs.org/docs/basic-features/data-fetching#when-is-fallback-true-useful

Example (fallback: false):

// pages/posts/[id].js

function Post({ post }) {
  // Render post...
}

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

  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false }
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

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

export default Post

Example (fallback: true):

// pages/posts/[id].js
import { useRouter } from 'next/router'

function Post({ post }) {
  const router = useRouter()

  // If the page is not yet generated, this will be displayed
  // initially until getStaticProps() finishes running
  if (router.isFallback) {
    return <div>Loading...</div>
  }

  // Render post...
}

// This function gets called at build time
export async function getStaticPaths() {
  return {
    // Only `/posts/1` and `/posts/2` are generated at build time
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    // Enable statically generating additional pages
    // For example: `/posts/3`
    fallback: true,
  }
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // Pass post data to the page via props
  return {
    props: { post },
    // Re-generate the post at most once per second
    // if a request comes in
    revalidate: 1,
  }
}

export default Post

getServerSideProps():

Leave a Comment