How to fetch data with static rendering in next.js
Tags: nextjs, webdev, tutorial
Published: 2021-08-16 08:48:24

Welcome to my first post on **How to?** series. In this article, I'm going to show step-by-step tutorials to getting Next.js to render your fetched content on a [statically generated](https://nextjs.org/docs/basic-features/pages) page. For doing this, we'll use _getStaticProps()_ function which will be covered in later sections.

At the end of tutorial, our page will look like this:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23thcqm6cevedii7t47s.png)

You can access the finished project [here.](https://github.com/D-Antonelli/nextjs-blog)

**Prerequisites**
- A WordPress instance with WPGraphQL plugin.

In these examples, I utilized WordPress [headless CMS](https://en.wikipedia.org/wiki/Headless_content_management_system) (content-management system) for managing blog contents and [WPGraphQL](https://www.wpgraphql.com/) API for fetching data. If you are interested in learning about how to configure Wordpress CMS from the scratch, I suggest you check out Rob Kendal's _Configuring WordPress as a headless CMS with Next.js_ article [series](https://dev.to/kendalmintcode/configuring-wordpress-as-a-headless-cms-with-next-js-3p1o). For this exercise, you are free to to fetch content from any headless CMS of your choice.
 
**Step 1: Creating a Next.js project**

We'll start by creating a new project by using [project create tool](https://nextjs.org/learn/basics/create-nextjs-app/setup) by Next.js. Open your terminal, and run the following command:

```
npx create-next-app nextjs-blog
```

Then, run this command:

```
cd nextjs-blog
```

`cd` stands for "change directory", so we are simply changing our current directory to the project folder we just created.

This step is optional for VS code users. When you run the command below, your project folder will automatically open on VS Code:

```
code .
```

In order to run our project on the development server, run this command:

```
npm run dev
```

Now your page will run on port 3000. This is how it looks:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ucq660gzv8igs3c1ot4n.png)


<br/>
**Step 2: Remove files**

Now open your project on any code editor you like (I prefer VS Code) and remove these files:

- /pages/api
- /pages

Alternatively, I generally find integrated terminal of VS Code very handy in managing the file operations. On VS Code, navigate to `View >  Terminal` menu command to open your terminal. There's no need to change directory as it'll directly start in the root of the project. Then, type this command:

```
rm -r pages/api
```

Now folder structure will look like this:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5xlqpzf974dzd7rgrbp.png)
<br/>
**Step 3: Add files**

- Add `/lib` folder in the root of the project.

- Add `/lib/api.js` file.

- Add `/.env.local` file in the root of the project.

<br/>
**Step 4: Saving local environment**

We have created `/.env.local` file for **managing our application environment constants**. These constants generally consist of URLs, API endpoints, and API keys. In our case, we will keep our URL in our local environment file. In `/.env.local`, type your  **unique** domain name of your WordPress instance with GraphQL endpoint:

```
WP_API_URL=http://www.your-custom-website.com/graphql
```

**Important Note**
Make sure that you restart the development server after making changes in the `/.env.local` file.
<br/>
**Step 5: Fetching data**

Now that we have got our _WP_API_URL_ variable covered, we can start writing our functions for fetching data. Navigate to `/lib/api.js` and add this line:

```
const API_URL = process.env.WP_API_URL;

async function fetchAPI(query, { variables } = {}) {
  const headers = { 'Content-Type': 'application/json' };

  const res = await fetch(API_URL, {
    method: 'POST',
    headers,
    body: JSON.stringify({ query, variables }),
  });

  const json = await res.json();
  if (json.errors) {
    console.log(json.errors);
    console.log('error details', query, variables);
    throw new Error('Failed to fetch API');
  }
  return json.data;
}
```

We've created this function to fetch the blog contents with GraphQL query parameter. You can consider this function as a reusable structure that can be utilized throughout different fetch requests.

Then, it's time to ask GraphQL to fetch our content by adding this function:

```
export async function getRecentPosts() {
  const data = await fetchAPI(`query getRecentPosts {
  posts(where: {orderby: {field: DATE, order: DESC}}, first: 6) {
    edges {
      node {
        id
        title
        slug
        featuredImage {
          node {
            altText
            mediaItemUrl
          }
        }
      }
    }
  }
}
`);

  return data?.posts;
}
```
<br/>
**Step 6: Change index file**

Now we will perform some changes in `pages/index.js`, which is the entry page. Remove everything between  `<main></main>` elements before pasting the code below, also remove `<footer></footer>`.

```
        <section>
          <div className={styles.articlesContainer}>
            <h1 className={styles.articleTitle}>latest articles</h1>
            <div className={styles.articleGrid}>
              {edges.map(({ node }) => (
                <div className={styles.article} key={node.id}>
                  <img
                    src={node.featuredImage.node?.mediaItemUrl}
                    alt={node.featuredImage.node?.altText}
                    className={styles.articleThumb}
                  />
                  <h2 className={styles.articleContent}>{node.title}</h2>
                </div>
              ))}
            </div>
          </div>
        </section>

```
We import fetch function on the top of the page, which we'll utilize to make a request for fetching blog contents.

```
import { getRecentPosts } from '../lib/api';
```

Add this parameter to the `Home` function component. 

`{ recentPosts: { edges } }`

This is how our `Home` component looks like now. Parameters will be covered in the next section.

```
import Head from 'next/head';
import styles from '../styles/Home.module.css';
import { getRecentPosts } from '../lib/api';

export default function Home({ recentPosts: { edges } }) {
  return (
    <div>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <section>
          <div className={styles.articlesContainer}>
            <h1 className={styles.articleTitle}>latest articles</h1>
            <div className={styles.articleGrid}>
              {edges.map(({ node }) => (
                <div className={styles.article} key={node.id}>
                  <img
                    src={node.featuredImage.node?.mediaItemUrl}
                    alt={node.featuredImage.node?.altText}
                    className={styles.articleThumb}
                  />
                  <h2 className={styles.articleContent}>{node.title}</h2>
                </div>
              ))}
            </div>
          </div>
        </section>
      </main>
    </div>
  );
}
```

Now it is time to add the Next.js function at the bottom of `/pages/index.js`: 

```
export async function getStaticProps() {
  const recentPosts = await getRecentPosts();
  return {
    props: {
      recentPosts,
    },
  };
}
```

**What role getStaticProps() function play here?**

Next.js has one very important concept: _pre-rendering_.
That means, instead of having client-side run JavaScript and generate HTML such as in _React_, Next.JS generates HTML _in advance_ and sends each generated page to client-side. Every page has been associated with a JavaScript code executed to make page interactive after being loaded by the browser. This is a process called _hydration_. You can make a simple experiment to see how this works: turn off JavaScript on your browser, run a Next.js project, and see the magic. HTML will still be rendered on your browser,  however, it'll not be interactive until JavaScript is turned on. 

_Static generation_, on the other hand, is one of the methods of pre-rendering that generates HTML at _build time_. Using _getStaticProps()_ function, we have generated a static page with data dependencies, in this case, our blog contents. We simply asked Next.js to fetch the data with async _getStaticProps()_ function. Then, Next.js produced a statically generated HTML passing the props of fetched data to the parameters of _Home_ component, and sent it to the client-side. That's it! Next.js will make **sure** that _data_ is fetched before the HTML is rendered, and all these happened in build-time. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3o2yyyjeourzfess7gxl.png)
_Image is taken from official Next.js website_ 

**One important note**

Please make sure you use _getStaticProps()_ function in a file located under `/pages` folder as it works **only in a _page_**.

Finally, our `/pages/index.js` page will look like this:

```
import Head from 'next/head';
import styles from '../styles/Home.module.css';
import { getRecentPosts } from '../lib/api';

export default function Home({ recentPosts: { edges } }) {
  return (
    <div>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <section>
          <div className={styles.articlesContainer}>
            <h1 className={styles.articleTitle}>latest articles</h1>
            <div className={styles.articleGrid}>
              {edges.map(({ node }) => (
                <div className={styles.article} key={node.id}>
                  <img
                    src={node.featuredImage.node?.mediaItemUrl}
                    alt={node.featuredImage.node?.altText}
                    className={styles.articleThumb}
                  />
                  <h2 className={styles.articleContent}>{node.title}</h2>
                </div>
              ))}
            </div>
          </div>
        </section>
      </main>
    </div>
  );
}

export async function getStaticProps() {
  const recentPosts = await getRecentPosts();
  return {
    props: {
      recentPosts,
    },
  };
}
```
<br/>
**Step 7: Change style files**

Open `/styles/globals.css`, delete all content, and paste this code:

```
*,
*::after,
*::before {
  margin: 0;
  padding: 0;
  box-sizing: inherit;
}

html {
  font-size: 62.5%; /* 1 rem = 10px; 10px/16px = 62.5% */
}

body {
  font-size: 2.5rem;
  box-sizing: border-box;
  line-height: 1.5;
}
```

Then, navigate to `/styles/Home.module.css`, delete all content, and paste this code:

```
.articleTitle {
  font-size: 3rem;
  text-transform: uppercase;
  text-align: center;
  letter-spacing: 0.12em;
}

.articleContent {
  font-size: 2.5rem;
  padding: 0 6.5rem;
  text-align: center;
  font-weight: 400;
}

.articlesContainer {
    padding: 6.5rem 4rem;
}

.articleGrid {
    margin-top: 6.5rem;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    row-gap: 6.5rem;
    column-gap: 3rem;
}

.articleThumb {
    width: 100%;
    height: 28rem;
    margin-bottom: 3rem;
}
```

And done! You've generated a static page with external data dependencies. 

I hope you find this article helpful. Thank you for reading.