Data fetching in Next.js


Next.js provides various ways to fetch data from an API or any other source. These methods can be used to either generate static pages(SSG) or for server-side rendering(SSR). getStaticProps, getStaticPaths and getServerSideProps are used for data fetching in Next.js. Using these techniques we can create SEO friendly websites with Next.js and pages will load much faster instead of making an API call every time and injecting content dynamically on the page which is a common limitation of a single page application or SPA.

Next js data fetching

Next.js provides different ways to fetch data which can be used to call APIs or any other data source which is used to perform different kinds of rendering as well.

Basically we use following two Next.js functions to fetch data-

  1. getStaticProps (For static generation): Used to fetch data at build time.
    getStaticPaths is used as a separate but supportive function for getStaticProps to help pre-render dynamic routes.
  2. getServerSideProps (For server-side rendering): Used to fetch data on every request.

getInitialProps is also used fetch data but it's not recommended to use in case of Next.js 9.3 or newer versions. You can read about this more here.

Let's talk about each one of them separately.

Basic setup

Let's have a basic setup so that we can explore things better.

npx create-next-app nextjs-blog
cd nextjs-blog

We will be using Axios to fetch data from an API. So install Axios

npm i axios

Then, run local development server

npm run dev

Now, open http://localhost:3000 to check if it's working.

You will see a page like this.

Nextjs welcome page

At this point your folder structure should look like this-

nextjs-blog/
|-- .next/
|-- node_modules
|-- pages/
|  |-- api/
|  |-- _app.js
|  |-- index.js
|-- public/
|-- styles/
|-- .gitignore
|-- package-lock.json
|-- package.json
|-- README.md

I am showing you only pages directory because this is the place where we will be working.

So, we are going to build a basic blog to understand static page generation and server side rendering by Next JS in which we will be using api provided by https://jsonplaceholder.typicode.com/.

Open pages/index.js and delete all the code present in this file.

Static generation getStaticProps

Let's understand what is static page generation first.

Basically, at the build time of Next JS i.e, when we run npm build or next build command then Next JS fetches all the data required on that page whether it is from an API or from anywhere else and generate a simple page with all those data, very much same as creating a HTML page by writing all the data in it directly.

And, then Next JS will serve those already created pages every time by which pages will be served much faster and no JavaScript will run to load content of page every time dynamically. This will help search engines to crawl the page with all of its content.

Basic structure of getStaticProps

export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  };
}

The context: parameter is an object containing the following keys:

  1. params: contains the route parameters for pages using dynamic routes.
  2. preview: is true if the page is in the preview mode and undefined otherwise.
  3. previewData: contains the preview data set by setPreviewData.
  4. locale: contains the active locale (if enabled).
  5. locales: contains all supported locales (if enabled).
  6. defaultLocale: contains the configured default locale (if enabled).

getStaticProps should return an object with:

  1. props: A required object with the props that will be received by the page component.
  2. revalidate: An optional amount in seconds after which a page re-generation can occur.
  3. notFound: An optional boolean value to allow the page to return a 404 status and page.

Now, add the following code into the pages/index.js file

// pages/index.js
import Axios from "axios";
import Link from "next/link";

const Index = (props) => {
  const posts = props.data;

  return (
    <div>
      <h1>Home</h1>
      <ol>
        {posts.map((post) => (
          <li key={post.id}>
            <Link
              href={{
                pathname: "/[id]",
                query: { id: post.id },
              }}
            >
              <a>{post.title}</a>
            </Link>
          </li>
        ))}
      </ol>
    </div>
  );
};

export default Index;

export const getStaticProps = async () => {
  const res = await Axios.get("https://jsonplaceholder.typicode.com/posts");
  return {
    props: { data: res.data.slice(0, 10) },
  };
};

Here, we are creating just a simple React component but here instead of receiving props from an parent component, we are receiving props from a function named getStaticProps.

Whatever we are writing inside the function getStaticProps will be executed at the build time and whatever props we are returning from this function will be received by the component.

Here, we are fetching some random posts from the API and slicing it to get just 10 posts.

Next js blog home page

Let's get some more information about getStaticProps

Now, create a file [id].js inside pages folder.
If you are not getting that why we are creating file name with square brackets [ ] then read about dynamic routing in NextJS here.

Now, paste the following code in pages/[id].js file.

// pages/[id].js
import Axios from "axios";
import { useRouter } from "next/router";

// router is required for fallback: true
const Post = ({ post }) => {
  const router = useRouter();

  if (router.isFallback) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>Post page</h1>
      <h2>{post.title}</h2>
      <p>{post.body}</p>
    </div>
  );
};

export default Post;

export const getStaticProps = async ({ params }) => {
  const { data } = await Axios.get(
    `https://jsonplaceholder.typicode.com/posts/${params.id}`
  );
  const post = data;
  return {
    props: {
      post,
    },
  };
};

export const getStaticPaths = async () => {
  const { data } = await Axios.get(
    "https://jsonplaceholder.typicode.com/posts"
  );
  const posts = data.slice(0, 10);
  const paths = posts.map((post) => ({ params: { id: post.id.toString() } }));
  return {
    paths,
    fallback: true,
  };
};

As before, we are using getStaticProps to fetch data from API which will help NextJS at build time.

Now go to our home page and click any of the blogs, you will be redirected to the respective blog post.

Nextjs post page

Here, we are receiving a parameter params in which we will get the id of the post to be fetched from the URL of current page.
After fetching data from API we are return it and receiving as props in the component.

But one thing you might have noticed here we are using an extra function getStaticPaths here.
Try to remove this function from this file, you will get error.

Understand the use of getStaticPaths

First of all note that, if getStaticProps receives a parameter i.e., the page has dynamic routes then it's necessary to define getStaticPaths.

NextJS force us to define getStaticPaths function because it wants to know what could be the other dynamic parameters are possible for which static page has to be created in advance.

After getting all these dynamic routes, NextJS fetch them too and create static page for them.

Basic structure of getStaticPaths

export async function getStaticPaths() {
    return {
    paths: [
        { params: { ... } }
    ],
    fallback: true or false
    };
}

We have to return paths in the following format

return {
    paths: [
    { params: { id: '1' } },
    { params: { id: '2' } }
    ],
    fallback: ...
}

Now, check our code above we are returning id of 10 posts from getStaticPaths function in string format, to create static page for them too.

You may notice a parameter fallback in the above code. Let's understand this now.

We are generating static page just for 10 posts which are having id 1 to 10.
But if you try http://localhost:3000/100 with id 100. Even that case NextJS will show us a post but after Loading... message.

This is happening because we have added fallback: true. It means that if static page is not available after NextJS build because we have not provided this id in getStaticPaths function as path then go check on the server if post with that id is available then serve it. If a post with this id is not even available on the server then only show Page not found error.

If we put fallback: false then NextJS will check for the id on the available pages i.e., the array of id returned by the paths variable and if no post is available there then it will show Page not found error directly without reaching to the server.

We are using useRouter to know whether it is a fallback request or not. This is how we can check the value of fallback.

Server side rendering getServerSideProps

If a web application uses server-side rendering then it has the ability to render web page on their server instead of calling an API from the browser and injecting dynamic data. Server-side sends full page and all of its content in HTML form which will be helpful for search engines to crawl.

To achieve such functionality, NextJS provides a function getServerSideProps.

Basic structure of getServerSideProps

export async function getServerSideProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  };
}

The context parameter is an object containing the following keys:

  1. params: If this page uses a dynamic route, params contains the route parameters.
  2. req: The HTTP IncomingMessage object.
  3. res: The HTTP response object.
  4. query: The query string.
  5. preview: preview is true if the page is in the preview mode and false otherwise.
  6. previewData: The preview data set by setPreviewData.
  7. resolvedUrl: A normalized version of the request URL that strips the _next/data prefix for client transitions and includes original query values.

The getServerSideProps should return an object with:

  1. props: A required object with the props that will be received by the page component.
  2. notFound: An optional boolean value to allow the page to return a 404 status and page.
  3. redirect: An optional redirect value to allow redirecting to internal and external resources.

Now use this is on our project. Delete all the code and paste following code in pages/[id].js

import Axios from "axios";

const Post = ({ post }) => {
  return (
    <div>
      <h1>Post page</h1>
      <h2>{post.title}</h2>
      <p>{post.body}</p>
    </div>
  );
};

export default Post;

export const getServerSideProps = async ({ params }) => {
  const { data } = await Axios.get(
    `https://jsonplaceholder.typicode.com/posts/${params.id}`
  );

  if (!data) {
    return {
      notFound: true,
    };
  }

  const post = data;
  return {
    props: {
      post,
    },
  };
};

As we did earlier, here we are fetching data inside getServerSideProps and return the data which willl be recieved by the component. The difference is that this function will be called everytime the page is requested and won't do anything at the build time.

Now go to our home page and click any of the blog, you will be redirected to the respective blog post.

Nextjs post page

This is how we can use perform either static page generation or server-side rendering in our web application in NextJS as per our requirement.