Next.js Dynamic Routing With getStaticPaths and getStaticProps

18 February 2022

Now that we can query data from a WordPress site using GraphQL, we’re going to learn how to output that data into our Next.js app. There are two functions provided by Next.js which can be used together to retrieve and display all our pages and their content.

The first, getStaticPaths, is used to create a dynamic route for every page on our WordPress website.

Second, getStaticProps will get the content for the specified page so that we can output it.

Create a local environment variable

The first step is to create a local environment variables file in our app which is a safe place to store any sensitive data. We’ll use this to create a reference to our WordPress URL. Create a file in the top directory of your NextJS app named .env.local.

.env.local
WP_API_URL=https://mywordpresswebsite.com/graphql

Handling GraphQL API calls

We’ll then create a new folder, also in the root of our project called lib and add a new file to it called api.js

This file will be used to query and return all of our GraphQL requests and responses to our WordPress site. At the top of this file, create a variable to hold our local environment URL for the WordPress site. Then we’ll create a function which can be used to send queries to our GraphQL endpoint.

lib/api.js
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.error(json.errors);
        throw new Error('Failed to fetch API');
    }

    return json.data;

}

The fetchAPI function takes two parameters – a GraphQL query and optional query variables.

Creating GraphQL query functions

Now that we have the ability to send GraphQL queries we’ll create the two functions that will create our dynamic routes and display the content.

The first function, getAllPages calls the fetchAPI function and passes in a GraphQL query as the only parameter to return the slug for all pages on the WordPress site and return that data.

lib/api.js
export async function getAllPages() {
    const data = await fetchAPI(`
        {
            pages {
                edges {
                    node {
                        slug
                    }
                }
            }
        }
    `);
    return data?.pages;
}

The second function, getPage takes a page parameter which will be the URI of the page we want to retrieve the content for. We’ll use this URI as a query variable in our GraphQL query then return the result.

lib/api.js
export async function getPage(page) {
    const data = await fetchAPI(`
        query PostBySlug($uri: String) {
            pageBy(uri: $uri) {
                title
                content
            }
        }
        `,
        {
            variables: {
                uri: page
            }
        }
    );
    return data;
}

Create Next.js page

Back in our pages directory, create a new file named [page].js and add a basic structure to the page.

pages/[page].js
import Layout from "../components/layout";

export default function Page({pageData}) {
    return (
        <layout>
            <h1>Page title</h1>
            <p>page content</p>
        </layout>
    )
}

Underneath this structure, still in our [page].js file, add the function getstaticPaths(). Within this function we call our getAllPages() function from the api.js file and save the returned data to a variable.

The getStaticPaths function returns an array with two values; paths which is an array of the slugs we query from GraphQL and fallback which we set to false which will return a 404 page for any paths not in that array.

pages/[page].js
export async function getStaticPaths() {
    const allPages = await getAllPages();
    return {
        paths: allPages.edges.map(({node}) => `/${node.slug}`) || [],
        fallback: false
    };
}

Now that we have a route for each page, we can use getStaticProps to retrieve the page data. The params parameter passed into the function is the slug of the page which we pass to the getPage function from api.js to be used as the query variable in the GraphQL query to return the page data. this page data is saved to the as a prop so that we can use it on our page.

pages/[page].js
export async function getStaticProps({params}) {
    const data = await getPage(params.page);
    return {
        props: {
            pageData: data.pageBy
        }
    };
}

At the top of [page].js we want to import the two functions from /lib/api.js so that they can be used by getStaticPaths and getStaticProps.

We also pass the props into the page in the pageData variable so that we can output the title and content of our page.

pages/[page].js
import Layout from "../components/layout";
import { getAllPages, getPage } from "../lib/api";

export default function Page({pageData}) {
    return (
        <layout>
            <h1>{pageData.title}</h1>
            <p>{pageData.content}</p>
        </layout>
    )
}

export async function getStaticPaths() {
    const allPages = await getAllPages();
    return {
        paths: allPages.edges.map(({node}) => `/${node.slug}`) || [],
        fallback: false
    };
}

export async function getStaticProps({params}) {
    const data = await getPage(params.page);
    return {
        props: {
            pageData: data.pageBy
        }
    };
}

If you run the dev environment and navigate to a valid URL you will see the page title and content being displayed.