Serverless Next.js At The Edge
Tags: serverless, nextjs, aws, cloud
Published: 2022-04-08 14:00:34

Originally posted at [Serverless](https://www.serverless.com/blog/serverless-nextjs/)

Today, I’m excited to announce a project I’ve been working on over the past few months. It provides a simple, fast and efficient way to deploy your Next.js applications to AWS using CloudFront, Lambda@Edge and S3.

The project is the [Serverless Next.js Component](https://github.com/danielcondemarin/serverless-next.js/tree/master/packages/serverless-nextjs-component) which you can use with the [Serverless Framework](https://github.com/serverless/serverless) to deploy Next.js apps to AWS Lambda@Edge functions in every CloudFront edge location across the globe. These Lambda@Edge functions do server-side rendering of your website pages, as close as possible to your end users, providing very low latency.

### Principles

The project was developed with a few design principles in mind.

**1. Zero configuration by default**

That’s right, you can get up and running in under a minute with no configuration required.

**2. Feature parity with next 9**

All features of next 9 are supported:

* [Server-side rendered pages](https://github.com/zeit/next.js#fetching-data-and-component-lifecycle). No surprise here, this is what attracts most folks to use next. serverless-next.js deploys your pages to Lambda@Edge. Server side rendering happens right at the edge, close to your users.

* [API Routes](https://nextjs.org/docs#api-routes). Like the pages, your api backend is also deployed to Lambda@Edge. When fetching data client side this ensures very low response latency.

* [Dynamic Pages / Route segments](https://nextjs.org/docs#dynamic-routing). Next recently introduced support in their [built-in routing system](https://nextjs.org/docs#routing) for [parameterised routes](https://nextjs.org/docs#dynamic-routes-support). serveless-next.js implements a lightweight router which is compatible with dynamic routes.

* [Automatic pre-rendering](https://nextjs.org/docs#automatic-prerendering). Some of your pages might not be SSR. These type of pages are compiled by next to HTML at build time. serverless-next.js takes advantage of this and deploys them to S3. Lambda@Edge in CloudFront then takes care of forwarding requests to static pages (e.g. /terms, /about, /contact etc.) to S3. These can be heavily cached.

* [Client assets](https://github.com/zeit/next.js/#cdn-support-with-asset-prefix). Build files generated by next such as webpack chunks, css files, etc. are uploaded to S3. In this case Lambda@Edge does not need invoking as any requests to _next/* is served by CloudFront from S3.

* [User static / public folders](https://github.com/zeit/next.js#static-file-serving-eg-images). User assets like images in the static folder or root level resources in your public folder are uploaded to S3. Like build assets, CloudFront serves these assets from S3.

**3. Fast deployments**

The [Serverless Next.js Component](https://github.com/danielcondemarin/serverless-next.js/tree/master/packages/serverless-nextjs-component) is fast. Deploying your application, typically takes less than a minute. next build is used behind the scenes, no magic there. A CloudFront distribution is provisioned for you with best practices in place. The pages compiled are zipped up and deployed to Lambda@Edge which is then associated to your distribution. An S3 bucket is also deployed for the static assets which are uploaded using [S3 accelerated transfers](https://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html).

The only caveat is that the first deployment you have to wait a few minutes for the CloudFront distribution to be available. However, subsequent deployments don’t have this problem. Once the distribution is up, deploying updates is fast.

CloudFormation **is not used** for provisioning resources. This is partly why deployments are quick. It also means the project is not bound by [CloudFormation limits](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html), which is an issue on the predecessor of this project, [serverless-nextjs-plugin](https://github.com/danielcondemarin/serverless-next.js/tree/master/packages/serverless-nextjs-plugin#caveats).

![](https://cdn-images-1.medium.com/max/5200/0*MgagF2xsvjP_Mb5A.png)

### Architecture

Let’s look in more detail at the architecture deployed to AWS.

![](https://cdn-images-1.medium.com/max/3840/0*5T88LTm7I4ahiFk6.png)

Three Cache Behaviours are created in CloudFront.

The first 2. _next/* and static/* forward the requests to S3.

The 3rd. is associated to a lambda function which is responsible for handling three types of requests.

 1. Server side rendered page. Any page that defines getInitialProps method will be rendered at this level and the response is returned immediately to the user.

 2. Statically optimised page. Requests to pages that were pre-compiled by next to HTML are forwarded to S3 where the HTML is stored.

 3. Public resources. These are requests to root level resources like /robots.txt, /favicon.ico, /manifest.json etc. These are also forwarded to S3 where these resources can be found.

The reason why 2. and 3. have to go through Lambda@Edge first is because these routes don’t conform to a pattern like _next/* or static/*. Also, one cache behaviour per route is a bad idea because CloudFront [only allows 25 per distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html).

### Getting Started

Using the [Serverless Next.js Component](https://github.com/danielcondemarin/serverless-next.js/tree/master/packages/serverless-nextjs-component) is easy, just add it to your serverless.yml like this:
{% gist https://gist.github.com/richardgrantserverless/c973860a90623699a6f5042ac220b239.js %}
Sane defaults are baked in, so no additional configuration is needed.

Use the Serverless Framework to deploy via the serverless command, like this:
{% gist https://gist.github.com/richardgrantserverless/7b14f7af5e04fa8cdb7494441cea8449.js %}
Remove it with the remove command:
{% gist https://gist.github.com/richardgrantserverless/d8e97fd93b3e8f1d643ae81c06198f0b.js %}
### Custom Domains

You can set a custom domain for your application. serverless-next.js takes care of associating the domain with your CloudFront distribution, creates the sub domain in Route53 and even sets up the SSL Certificate using AWS ACM. It is optional and looks like this:
{% gist https://gist.github.com/richardgrantserverless/5845a93d4c40295982956a892513a6d3.js %}
### Behind the Scenes

The project is powered by the amazing [serverless-components](https://serverless.com/blog/what-are-serverless-components-how-use/). At its core it uses 4 components:

* [@serverless/aws-s3](https://github.com/serverless-components/aws-s3)

* [@serverless/aws-cloudfront](https://github.com/serverless-components/aws-cloudfront)

* [@serverless/aws-lambda](https://github.com/serverless-components/aws-lambda)

* [@serverless/domain](https://github.com/serverless-components/domain)

Most of the heavy lifting is done by the components themselves, serverless-next.js simply orchestrates.

### What’s Next?

Build time efficiencies, configurable caching options for users and potentially adding a separate /api cache behaviour for API Routes. I will also be working on more complete examples that integrate with other AWS Services.

Hope you find the project useful. If you find any issues or would like to see a new feature please [raise an issue](https://github.com/danielcondemarin/serverless-next.js/issues). Also, contributions are welcome :)