Part 4: Data Fetching in Next.js

Part 4: Data Fetching in Next.js

Everything you need to know about Next.js

Overview

Welcome on board! In this series of articles, you'll learn everything you need to know about Next.js through project-based learning so you can get started with Next.js. If you have no idea what Next.js is, I highly recommend you to check out my newly released post here. Before we dive in, here is a sneak peek of what we're going to build:

2021-11-06 11-52-11.gif

If you've missed Part 3, I highly recommend you to check out Part 3 first before proceeding. Note that this will be the final part of our app, we're able to see the end product of it!!!

Data Fetching

There are tons of ways you can fetch data in Next.js. It depends on what you want to achieve when you need the data, and how you're going to use the data. You have the power in your hand.

Server Data

Let's fetch our server data! We'll need to use getServerSideProps, it'll be called at runtime at every request. You will have the runtime data like query params, HTTP headers, and the req and res objects from API handlers.

When to adopt what in Next.js?

Here are my recommendations:

Require data at runtime but don't need server-side rendering?

Make use of client-side data fetching.

Require data at runtime but do need server-side rendering?

Make use of getServerSideProps.

Pages rely heavily on data that is cachable and accessible at build time (for example a Content Management System)?

Make use of getStaticProps.

Implementing getServerSideProps

Enough of talking, now head to /pages. Inside /pages, you'll need to create a directory called files. Inside /pages/files, you'll need to create two files [id].js and index.js.

Untitled.png

Time to implement getServerSideProps, open up /pages/files/index.js:

// /pages/files/index.js

import * as React from "react";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid";
import Link from "next/link";

function File({ files }) {
  return (
    <div>
      <Box sx={{ display: "flex", justifyContent: "center", p: 1, m: 3 }}>
        <Typography variant="h4" gutterBottom component="div">
          All Your Files Below
        </Typography>
      </Box>

      <Grid
        container
        direction="row"
        justifyContent="center"
        alignItems="center"
      >
        {files.map((file) => (
          <Grid item xs={6} key={file.id}>
            <Box p={5}>
              <Link href="/files/[id]" as={`/files/${file.id}`} passHref>
                <a sx={{ textDecoration: "none", cursor: "pointer" }}>
                  <Paper
                    sx={{
                      textAlign: "center",
                      height: 60,
                      lineHeight: "60px",
                    }}
                    elevation={5}
                  >
                    <strong>{file.title}</strong>
                  </Paper>
                </a>
              </Link>
            </Box>
          </Grid>
        ))}
      </Grid>
    </div>
  );
}

export default File;

// Make use of getServerSideProps here
export async function getServerSideProps() {
  const res = await fetch("http://localhost:3000/api/file");
  const { data } = await res.json();

  return {
    props: { files: data },
  };
}

By using getServerSideProps in /pages/files/index.js, this will allow data to fetch at every request. Data will be fetched and can be returned as props then passed to the component above.

Tips when using getServerSideProps:

Don't use getServerSideProps unless absolutely necessary. It's because it computes every request, it may be slow.

In our case, our props are named files. Inside File component, we iterate through it using .map() iterator and display the data to the UI like so:

localhost_3000_files.png

Next up, head to /pages/files/[id].js, this is where we are going to make the path dynamic using getServerSideProps. Think about it, what will happen when we click on one of the files? It'll generate the path based on the id given in the database.

Time to implement the code:

// /pages/files/[id].js

import React from "react";

function DynamicFile({ file }) {
  return (
    <div>
      <h1>File Title: {file.title} </h1>
    </div>
  );
}

export default DynamicFile;

export async function getServerSideProps({ params, req, res }) {
  const response = await fetch(`http://localhost:3000/api/file/${params.id}`);

  // Power of Next.js
  if (!response.ok) {
    res.writeHead(302, { Location: "/files" });
    res.end();
    return { props: {} };
  }

  const { data } = await response.json();

  return {
    props: { file: data },
  };
}

Inside getServerSideProps, we fetch some files from our API. If the request fails, you'll be redirected back to the /files path. Then, we destructure the data into a JSON response and pass it up to the component as props named file.

Static Data

By using getStaticProps, Next.js will run this function at build time. Whatever your return as props will be passed into the exported page.

This function, as well as all other data fetching functions, will only ever be executed on the server. The actual code won't even be bundled with the client code. This means you can do some amazing things here like:

  • Work with file system
  • Connect to a database
  • Crawl a website

Let's use the getStaticProps function, we'll write a code that will stimulate a Content Management System (CMS) in our app.

This code will stimulate a CMS:

export function getStaticProps() {
  return {
    props: {
      content: {
        title: "Hey, Welcome to",
      },
    },
  };
}

Head to /pages/index.js, then implement the code like so:

import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import styles from "../styles/Home.module.css";

export default function Home({ content }) {
  return (
    <div className={styles.container}>
      <Head>
        <title>File App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          {content.title} <a>File App!</a>
        </h1>

        <div className={styles.grid}>
          <Link href="/files">
            <a className={styles.card}>
              <h2>Library &rarr;</h2>
              <p>Checkout all your files here.</p>
            </a>
          </Link>
        </div>
      </main>

      <footer className={styles.footer}>
        <a
          href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{" "}
          <span className={styles.logo}>
            <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
          </span>
        </a>
      </footer>
    </div>
  );
}

// Mock CMS
export function getStaticProps() {
  return {
    props: {
      content: {
        title: "Hey, Welcome to",
      },
    },
  };
}

The final product of the app will look and function the same as in the gif above. In case, you missed it:

2021-11-06 11-52-11.gif

Conclusion

Let's wrap things up! When it comes to data fetching, Next.js just continues getting better; it's by far my favorite part. It's superb!! There is little to no overhead and yet it's highly powerful. It makes it simple to create open-source that can integrate with Next.js. That's the end of our app.


Reference

Final source code for this project

Learn Next.js

Next.js Data Fetching

What is a CMS?