Data fetching in Next.js with getServerSideProps and getStaticProps

Matt Angelosanto - Sep 12 '23 - - Dev Community

Written by Yomi Eluwande✏️

In Next.js, data fetching methods play a crucial role in enabling server-side rendering (SSR), static site generation (SSG), and client-side data fetching for your React components. These methods allow you to fetch data from various sources (e.g., APIs, databases, external services) and use that data to pre-render pages or hydrate them on the client side.

In this article, we’ll explore the getInitialProps, getServerSideProps, and getStaticProps data fetching methods. We’ll take a look at the role getInitialProps played in previous versions of Next.js and the transition to newer and better data fetching methods.

Jump ahead:

Understanding getInitialProps

getInitialProps is a method used in older versions of Next.js (versions prior to 9.3) to fetch data on the server side before rendering a page. It was the primary data fetching method used in Next.js before newer data fetching methods like getServerSideProps and getStaticProps were introduced.

In older versions of Next.js, getInitialProps was the primary data fetching mechanism for both SSR and client-side rendering. It allowed developers to perform custom data fetching logic for each page and pass the fetched data as props to the page component:

// In older versions of Next.js, you would define getInitialProps like this:

import React from 'react';

const HomePage = ({ posts }) => {
  // Render the list of posts
  return (
    <div>
      <h1>Latest Blog Posts</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
};

HomePage.getInitialProps = async () => {
  // Fetch data from an API
  const response = await fetch('https://api.example.com/posts');
  const posts = await response.json();

  // Return the data as props
  return { posts };
};

export default HomePage;
Enter fullscreen mode Exit fullscreen mode

In this example, the getInitialProps method fetches the list of blog posts from the API server-side. When a user visits the homepage, the server will pre-render the page with the fetched data and serve it as a complete HTML page. This approach helps with SEO, improves initial page load times, and ensures that the page has the necessary data before being served to the client.

Transitioning to getServerSideProps and getStaticProps

The transition from getInitialProps to getServerSideProps and getStaticProps in Next.js represented a significant improvement in data fetching and rendering strategies. This change was introduced to simplify data fetching, enhance performance, and provide better predictability in terms of when and where data is fetched.

Let’s start by looking at what the getServerSideProps and getStaticProps lifecycles are useful for, and how they each mark pages for rendering.

getServerSideProps is a data fetching method that was introduced in Next.js 9.3. It is used specifically for server-side rendering (SSR). Unlike getInitialProps, getServerSideProps is only executed on the server side during the initial page request and not on subsequent client-side navigations. This change improves performance by reducing duplicate data fetching and provides better predictability of server-side data fetching.

getStaticProps is another data fetching method that was introduced in Next.js 9.3, and it is used for static site generation (SSG). When using getStaticProps, Next.js pre-renders the page at build time and fetches the data during the build process. The pre-rendered HTML pages are then served to users directly from the CDN, offering faster page loads and reducing server load.

These new data fetching methods ensured better performance by reducing unnecessary data fetching and improving the performance of your Next.js application by fetching data more intelligently. With getServerSideProps, you know that data is fetched only during the initial server-side request, making it easier to understand when and where data is retrieved, and with getStaticProps, the data will only be fetched at build time.

With the introduction of getStaticProps and getServerSideProps, Next.js users now have two different data fetching methods that are used to mark pages for rendering either at build time or upon each request respectively.

When a user requests a page that uses getServerSideProps, the server will execute this function, fetch the data, and pass it as props to the page component. The page is then rendered on the server with the fetched data and sent to the client as a complete HTML page. This approach is known as server-side rendering (SSR), and it ensures that the page always has the most up-to-date data when accessed by users.

With getStaticProps, you get the option to pre-render pages at build time, generating static HTML files with the fetched data. When you use getStaticProps, Next.js runs the function during the build process (not on each request) and fetches the data needed for the page. The fetched data is then used to pre-render the page into a static HTML file, which is saved in the build output directory (e.g., the build folder).

Now, when a user visits a page that was pre-rendered with getStaticProps, Next.js serves the static HTML page directly from a CDN, improving page load times and reducing server load. This approach is called popularly known as static site generation (SSG), and it is ideal for pages with content that doesn't change frequently.

A deep dive into the getServerSideProps lifecycle

As mentioned above, the getServerSideProps lifecycle is used to fetch data on the server side and pre-render the page with the fetched data before sending it to the client. Unlike getStaticProps, getServerSideProps executes on each request, making it suitable for pages that require dynamic data or data that changes frequently.

The getServerSideProps function takes a context object as a parameter that contains page data such as params, res, req, query, etc. Here's a breakdown of how the getServerSideProps lifecycle works:

  1. When a user requests a page that uses getServerSideProps, the server-side code is executed first
  2. The getServerSideProps function is called, and it fetches the necessary data from external APIs, databases, or other sources
  3. The fetched data is then passed as props to the page component
  4. The page is pre-rendered on the server with the fetched data and sent as a complete HTML page to the client
  5. The client-side JavaScript takes over once the page is loaded, and any subsequent interactions are handled on the client side

When compared to the getInitialProps lifecycle, getServerSideProps is much cleaner and more predictable in terms of fetching data, especially when it comes to context switching between server operations and client operations.

With getInitialProps, operations were being executed on both the server and client side, which sometimes led to duplicate data fetching during client-side navigation. But with getServerSideProps, there was a clear understanding that data fetching logic in the getServerSideProps lifecycle would be executed only on the server side during the initial request, eliminating the duplicate data fetching that might occur with getInitialProps.

Example use cases for getServerSideProps:

  • Real-time data: If your page requires real-time data that changes frequently (e.g., live chat messages, real-time analytics), you can use getServerSideProps to fetch the latest data on each request
  • Authenticated data: When dealing with authenticated data that is user-specific and changes based on the user's session or permissions, getServerSideProps is well-suited for fetching this data securely on the server side
  • Dynamic pages: For pages with dynamic routes, where the content depends on the route parameters, getServerSideProps can fetch the data for the specific route during each request

In summary, getServerSideProps offers a more straightforward and efficient way to fetch data on the server side, especially for pages that require server-side rendering and frequently changing data. It provides better control over data fetching, improved performance, and a smoother transition from the getInitialProps lifecycle.

The role of getInitialProps in Next.js 13

Despite the transition to newer data fetching methods, Next.js has maintained support for getInitialProps for backward compatibility. This is because there might be existing projects that rely on getInitialProps and transitioning all of them to the newer methods might be impractical in some cases.

To handle edge cases where getInitialProps might still be necessary, consider the following scenarios:

  • Complex data fetching logic: If your data fetching logic is complex and can't be easily achieved with getStaticProps or getServerSideProps, you might continue using getInitialProps
  • Custom SSR logic: If you need custom server-side rendering logic that can't be achieved with the newer methods, you might choose to use getInitialProps
  • Legacy projects: If you're working on a legacy project that was built using older versions of Next.js and heavily relies on getInitialProps, it might be more practical to continue using it rather than rewriting everything

Optimizing getServerSideProps for performance in Next.js 13

getServerSideProps runs on every request, which means that if you're doing a lot of data fetching or computations, it can slow down your site. There are a few things you can do to optimize the use of getServerSideProps for performance in Next.js 13.

One is to make sure you're returning the required props object with the props property, which can help reduce the number of repetitive calls. When you call getServerSideProps, Next.js creates a server-side request to fetch the data. This request can be quite expensive, especially if you're doing a lot of computations. If you return the required props object, Next.js will memoize the request and reuse the result for subsequent calls. This is important because it means that Next.js doesn't have to make multiple requests to the server to get the same data, which can significantly improve performance.

Another way to optimize the use of getServerSideProps in your project is by taking advantage of smarter caching. It works by caching the results of your request based on the content that was requested. For example, if a user requests a specific page, Next.js will only cache the data for that page. This means that if another user requests a different page, it won't have to load the entire site from scratch, which can save a lot of time. This is different from traditional caching, which simply stores the entire site in a cache and serves it to all users.

Another benefit of smarter caching is that it allows for "incremental updates". This means that if you make a small change to your code, only the affected parts of the site will be updated in the cache.

Migrating from getInitialProps to getServerSideProps or getStaticProps

Migrating from getInitialProps to getServerSideProps or getStaticProps involves updating your data fetching logic to the new data fetching methods introduced in Next.js. The migration process depends on whether you want to achieve server-side rendering (SSR) or static site generation (SSG). Here's a detailed overview of the steps involved in the migration:

Migrating to getServerSideProps (SSR):

  1. Remove getInitialProps: If your page components use getInitialProps, you need to remove it from those components
  2. Replace getInitialProps with getServerSideProps: Replace the getInitialProps method with the getServerSideProps method in your page components. The structure of getServerSideProps is different from getInitialProps, and it returns an object with the props key containing the fetched data
  3. Pass props to the component: With getServerSideProps, the fetched data will be automatically passed as props to the page component. You can access this data using the props parameter of your page component

Let's consider a simple example where you have a blog application, and you want to fetch a list of blog posts from an external API to display on the homepage:

// Before migrating from getInitialProps

const HomePage = ({ posts }) => {
  // Render the list of posts
};

HomePage.getInitialProps = async () => {
  // Fetch data from an API
  const response = await fetch('https://api.example.com/posts');
  const posts = await response.json();

  // Return the data as props
  return { posts };
};

// After migrating to getServerSideProps

const HomePage = ({ posts }) => {
  // Render the list of posts
};

export async function getServerSideProps() {
  // Fetch data from an API
  const response = await fetch('https://api.example.com/posts');
  const posts = await response.json();

  // Return the data as props
  return {
    props: { posts },
  };
}
Enter fullscreen mode Exit fullscreen mode

Migrating to getStaticProps (SSG):

  1. Remove getInitialProps: If your page components use getInitialProps, you need to remove it from those components
  2. Replace getInitialProps with getStaticProps: Replace the getInitialProps method with the getStaticProps method in your page components. Like getServerSideProps, the structure of getStaticProps is different from getInitialProps, and it returns an object with the props key containing the fetched data
  3. Data revalidation (optional): With getStaticProps, you can also include the revalidate option in the returned object to specify how often the data should be revalidated and regenerated. This is useful when you want to update the data periodically without redeploying your application

Using the same example use case as above, here is our code before and after migrating from getInitialProps and getStaticProps:

// Before migrating from getInitialProps

const HomePage = ({ posts }) => {
  // Render the list of posts
};

HomePage.getInitialProps = async () => {
  // Fetch data from an API
  const response = await fetch('https://api.example.com/posts');
  const posts = await response.json();

  // Return the data as props
  return { posts };
};

// After migrating to getStaticProps

const HomePage = ({ posts }) => {
  // Render the list of posts
};

export async function getStaticProps() {
  // Fetch data from an API
  const response = await fetch('https://api.example.com/posts');
  const posts = await response.json();

  // Return the data as props
  return {
    props: { posts },
  };
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

In summary, migrating from getInitialProps to getServerSideProps or getStaticProps involves updating the data fetching logic and method signatures to improve performance, predictability, and context switching in your Next.js application. Depending on your use case, you can choose between getServerSideProps for server-side rendering or getStaticProps for static site generation.


LogRocket: Full visibility into production Next.js apps

Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket Signup

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your Next.js app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your Next.js apps — start monitoring for free.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .