Dynamic routing for Now using NextJS
Tags: nextjs, nowjs
Published: 2019-12-10 08:09:53

When I looked into Server Side Rendering (SSR) for React, I quickly encountered [NextJS](https://www.nextjs.org). Getting the hang of it took me some time, but since then it's become a vital part of our digital products. Quickly I came across a very basic requirement: dynamic routing for blog posts.

### The objectives

1. Informing Now about dynamic routes
2. Fetching a blog post using Next's `getInitialProps` method
3. Using a HOC to render a custom error page when data can't be found

### 1. Informing Now about dynamic routes

At [OOGT](https://www.oogt.nl), we deploy our applications to [Now](https://www.zeit.co). When you're using NextJS, deploying can be as simple as running `now`. It also comes with a handy `now dev` command so that you can locally mimic your Now deployment.

Now needs some knowledge about your application, and you can provide it through a `now.json` file. This typically looks something like this:

```json
// now.json
{
  "name": "dynamic-routing",
  "version": 2,
  "builds": [{ "src": "next.config.js", "use": "@now/next" }],
  "routes": [
    { "src": "/", "dest": "/index" },
    {
      "src": "/_next/static/(?:[^/]+/pages|chunks|runtime)/.+",
      "headers": { "cache-control": "immutable,max-age=31536000" }
    }
  ]
}
```

Here, the `name` is the name of your Now project. The `version` field is the version of Now deployments you're using. The `builds` fields holds some configuration about your builder, which we won't go into right now. Finally, the `routes` field is what we're interested in right now. We'll add a new entry to this.

```json
// now.json
{
  "name": "dynamic-routing",
  "version": 2,
  "builds": [{ "src": "next.config.js", "use": "@now/next" }],
  "routes": [
    { "src": "/", "dest": "/index" },
    { "src": "/(?<slug>[^/]+)", "dest": "/post?slug=$slug" },
    {
      "src": "/_next/static/(?:[^/]+/pages|chunks|runtime)/.+",
      "headers": { "cache-control": "immutable,max-age=31536000" }
    }
  ]
}
```

What this entry will do is match any URLs after the `/`, and reroute them to the `Post` page, where `slug` is a property of the `query` object in the request context. So, the URL in the browser will be `/some-blog-post-title`, but under the hood it renders the `post` page component with a value of "some-blog-post-title" for the `slug` property in the `query` object. Sounds more difficult than it actually is:

```js
// pages/post.js
import React from 'react';

const Post = ({ slug }) => <div>{slug}</div>;

Post.getInitialProps = async ({ query }) => {
  const { slug } = query;

  return {
    slug
  }
}

export default Post;
```

In the code above, the `getInitialProps` method provided by NextJS can be used to do some async task like data fetching. It returns an object with the props that can be used by our functional Post component.

Next up, let's use this slug to fetch a blog post!

### 2. Fetching a blog post

In this example, we'll pretend we have a Wordpress instance running somewhere which contains the blog posts. We're going to use the REST API to fetch a blog post. We'll use `axios` for the requests since this works both server and client-side.

```js
// data/posts.js
import axios from 'axios';

export const getPost = async (slug) => {
  const response = await axios.get(`https://www.mywordpress.com/wp-json/wp/v2/posts?slug=${slug}`);

  if (!response.data.length) {
    return {
      statusCode: 404,
    };
  }

  return {
    statusCode: 200,
    post: response.data[0],
  };
};
```

Notice how the returned object contains a `statusCode` of 404 if the returned response's `data` object doesn't contain any items. This is an indicator that the blog post does not exist. Otherwise, we'll return a `statusCode` of 200 and a `post` property containing the actual data.

**Question to self: does the Wordpress REST API actually return a statusCode of 200 for a non-existent post? It would be easier to just use the returned statusCode instead of assuming an empty response means it does not exist. Also, isn't there an easier way of fetching a single post instead of using the first item in the returned array?**

Now, we can use the `getPost` method in our Post component

```js
// pages/post.js
import React from 'react';

import { getPost } from '../data/posts';

const Post = ({ statusCode, post }) => {
  if (statusCode !== 200) {
    return (
      <div>
        <h1>Oops</h1>
        <p>Something has gone wrong</p>
      </div>
    );
  }

  const { title: { rendered: title }, slug } = post;

  return (
    <div>
      <h1>{title}</h1>
      <p>{slug}</p>
    </div>
  );
}

Post.getInitialProps = async (slug) => {
  const { statusCode, post } = await getPost(slug);

  return {
    statusCode,
    post,
  }
}

export default Post;
```

Now, the above will work just fine for this particular use case. However, imagine we have another page rendering some post that may or may not exist. We could put an if-statement in every page component checking the statusCode, but we can also use a Higher Order Component (HOC) to render an error page whenever some data fetching method returns a statusCode other than 200.

### 3. Using a HOC to render a custom error page

You can define a custom error page by creating a file `_error.js` in the `pages` directory. A simple error page could look something like this:

```js
// pages/_error.js
import React from 'react';

const Error = ({ statusCode }) => {
  let errorMessage = 'An unexpected error occured';

  if (statusCode === 404) {
    errorMessage = 'Page could not be found';
  }

  return (
    <div>
      <h1>Something went wrong</h1>
      <p>{errorMessage}</p>
    </div>
  )
}

export default Error;
```

To render the error page I found this HOC @tneutkens mentioned which makes it very easy to reuse a custom error page.

```js
// hoc/withError.js
import React from 'react';

import Error from '../pages/_error';

export default Component => class extends React.Component {
  static async getInitialProps(ctx) {
    const props = await Component.getInitialProps(ctx);
    const { statusCode } = props;

    return { statusCode, ...props };
  }

  render() {
    const { statusCode } = this.props;
    if (statusCode && statusCode !== 200) {
      return <Error statusCode={statusCode} {...this.props} />;
    }
    return <Component {...this.props} />;
  }
};
```

We can now use this for our page like this:
```js
// pages/post.js
import React from 'react';

import withError from '../hoc/withError';
import { getPost } from '../data/posts';

const Post = ({ post }) => {
  const { title: { rendered: title }, slug } = post;

  return (
    <div>
      <h1>{title}</h1>
      <p>{slug}</p>
    </div>
  );
}

Post.getInitialProps = async (slug) => {
  const { statusCode, post } = await getPost(slug);

  return {
    statusCode,
    post,
  }
}

export default withError(Post);
```

## Got questions or feedback?

Do you know of an easier way of doing this? Or do you have any questions? I'm not claiming to be an expert in Now/NextJS whatsoever, but perhaps I can help digging into a problem you might run into.

Don't forget to start following me here, on [Medium](https://medium.com/@robbertvancaem) or on [Twitter](https://twitter.com/robbertvancaem)!