Deploying with Custom Serverless Next.js Routing

Migrate from a Next.js application with a custom server to a serverless Next.js application using Now routes.

Next.js offers developers a built-in routing system that allows you to link to other pages whilst also giving those pages a custom URL. When a user refreshes the custom URL, however, the browser will not be able to load the link because the page was previously routed client-side. To resolve this, we need to also handle routing server-side.

Note: If you have yet to build an application with Next.js, look no further than the Next.js interactive tutorial website or the documentation. Deploy your Next.js app with the @now/next Builder.

Preface: Next.js Routing

When you link to a page within the same Next.js application, you can provide two options. The first and required option is href. This is a link to the page as it exists in the pages directory. The second is as which describes the URL Next.js should show when routing to that page.

For example, if you have a page located at pages/product.js in your codebase, you can use the href option with the value /product?name=<product_name> to route and render that page with a query parameter, however, if you want to show the URL /product/<product_name>, you can use the as option to format the URL when routing.

This is an amazing feature of Next.js, however, it cannot control how the server routes to the application if the routes are not defined with the server.

The Old Custom Server Method

The solution to this problem was previously using a custom server. For example, you could create an Express application and catch the request to /product/<product_name> and route it to the /product?name=<product_name> file behind the scenes so that you can keep the custom URL:

server.get('/products/:name', (req, res) => {
  return app.render(req, res, '/product', { name: req.params.name })
})

An example custom server route capture and render method.

The New Routes Method

With Now 2.0 and the serverless approach, each Next.js page is a separate entry point, making the sole custom server entry point a thing of the past for the good of performance and stability. Fortunately, Now 2.0 offers a solution, by way of the routes configuration.

The following now.json configuration enables the deployment to use the Now platform version 2.0 and defines the routes that the deployment should acknowledge when receiving a request to an entry point.

{
  "version": 2,
  "routes": [
    { "src": "/product/(?<name>[^/]+)$", "dest": "/product?name=$name" }
  ]
}

Using Now 2.0 routes configuration to rewrite URLs to the rendered Next.js page with an embedded capture group to route query parameters.

Note: The above example now.json file does not include a builds option which is necessary for building a Next.js application with Now. Please see the Next.js Builder documentation for more information.

By Example

If you are yet to set up routing for your Next.js app, this brief example describes how to use Next.js routing with Now routes to support custom URLs.

Step 1: Defining Pages

The first part of creating defined routes is to make sure those routes can be rendered as pages. In this example, we will have two pages, "index" (our homepage) and "product".

The index.js inside of our pages directory is just a small page that exports a heading and a paragraph of text with a link to our future products page using a Next.js Link component:

import Link from 'next/link'

export default () => (
  <div>
    <h1>Welcome to our Next.js website!</h1>
    <p>
      View our{' '}
      <Link href="/product?name=espresso">
        <a>espresso product</a>
      </Link>
      !
    </p>
  </div>
)
Note: In a real application, the product name query parameter would most likely be dynamic based on other factors, but for simplicity, we'll use "espresso" as the product name.

Next, for our link to work, we need to create a product.js file in our pages directory. This page displays the query parameter, which we receive with the Next.js getInitialProps lifecycle method, as the title and a simple paragraph:

const Product = ({ name }) => (
  <div>
    <h1>{name}</h1>
    <p>Welcome to our product page for {name}!</p>
  </div>
)

Product.getInitialProps = async ({ query }) => {
  return { name: query.name }
}

export default Product

Step 2: Defining a Custom URL

With the pages set up, we can now define the product page to have a custom URL. The first part of this is to extend the Link component usage in the index page to contain an as attribute that defines our custom URL. Taking the line consisting of a paragraph and a Next.js link in the index.js file, we can add the as attribute with a custom URL.

import Link from 'next/link'

export default () => (
  <div>
    <h1>Welcome to our Next.js website!</h1>
-   <p>View our <Link href="/product?name=espresso"><a>espresso product</a></Link>!</p>
+   <p>View our <Link href="/product?name=espresso" as="/product/espresso"><a>espresso product</a></Link>!</p>
  </div>
)

A diff of the changes made to the index.js page file.

Now, clicking the espresso product link will result in the URL path being /product/espresso while rendering the product page with the name query parameter set to espresso.

Step 3: Defining routes configuration

Note: The prerequisite for this configuration is to host your Next.js application on Now. Read the @now/next Builder documentation to get started.

When refreshing the URL with the path of /product/espresso, or accessing it directly, you will find that the server will return a 404 response. This is because there is no entry point that exists for that path. Next.js handles the routing to that page internally on the client-side. The next step is to provide Now with the context of this path to route to the /product page entry point.

For this context, we can use the routes configuration option inside of a now.json file:

{
  "version": 2,
  "routes": [
    { "src": "/product/(?<name>[^/]+)$", "dest": "/product?name=$name" }
  ]
}

An example now.json configuration that rewrites our custom path to the product page with a query parameter.

Note: The above configuration omits the builds property which is necessary for Now to build and serve a Next.js application. Please see the Next.js Builder documentation for more information.

The routes property, as exampled above, allows us to define a named capture group that gets the query parameter from the URL path and enables the usage of that parameter in the destination path by using the capture ID prefixed by a dollar sign, in our case: $name.

Step 4: Deploying

With the Next.js application setup complete and Now configured to accept the custom routes, we can finally deploy our application using Now and the Next.js Builder.

now

If you are using Now for GitHub; pushing your code to your repository is all you need to do for Now to automatically deploy your application.

Once you deploy, you will receive a deployment URL, similar to the following which contains this example with a few styling tweaks for ease on the eyes: https://next-routes-2s2lop08x.now.sh/

The full source of the deployment is also available here: https://next-routes-2s2lop08x.now.sh/_src

Read More


How Was This Guide?

Written By
Written by timothytimothy
Written by timneutkenstimneutkens
on January 23rd 2019