sladg / nextjs-lambda

Lambda deployments for Nextjs12 & Nextjs13 (standalone). Easy CLI commands to get your standalone Next output to run in AWS Lambda (not @Edge)! Uses CDK in behind and produces code zips importable to Terraform, Serverless, Azure, etc.
MIT License
173 stars 19 forks source link
aws-cdk aws-cdk-construct aws-cdk-typescript aws-lambda bundler image lambda next12 next13 nextjs nextjs-12 nextjs-13 serverless

NextJS Lambda Utils

This is a project allowing to deploy Next applications (standalone options turned on) to AWS Lambda without hassle.

This is an alternative to existing Lambda@Edge implementation (see) as it has too many limitations (primarily inability to use env vars) and deployments take too long.

This library uses Cloudfront, S3, ApiGateway and Lambdas to deploy easily in seconds (hotswap supported).

Multiple domains can be specified within CLI and Routing as well as Certificates are handled automatically.

TL;DR

next.config.js

const path = require('path')

module.exports = {
    compress: false,
    output: 'standalone',
    experimental: {
        esmExternals: false, // optional
        externalDir: true, // optional
        outputFileTracingRoot: path.join(__dirname, '../../'), // monorepo option
    }
}

Usage

We need to create 2 lambdas in order to use NextJS. First one is handling pages/api rendering, second is solving image optimization.

This division makes it easier to control resources and specify sizes and timeouts as those operations are completely different.

Loading of assets and static content is handled via Cloudfront and S3 origin, so there is no need for specifying this behaviour in Lambda or handling it anyhow.

Server handler

This is a Lambda entrypoint to handle non-asset requests. We need a way to start Next in lambda-friendly way and translate ApiGateway event into typical HTTP event. This is handled by server-handler, which sits alongside of next's server.js in standalone output.

Image handler

Lambda consumes ApiGateway requests, so we need to create ApiGw proxy (v2) that will trigger Lambda.

Lambda is designed to serve _next/image* route in NextJS stack and replaces the default handler so we can optimize caching and memory limits for page renders and image optimization.

Optimizer used: imaginex

Environment variables

If using CDK, you can easily pass environment variables to Lambda. If .env file is present during build time, this will get picked up and passed to Lambda as file.

If env variables with prefix NEXT_ are present during deployment time, those will get picked up and passed as environment variables to Lambda.

The priority for resolving env variables is as follows (stopping when found):

Frontend environment variables are automatically resolved during build time! You will not be able to set NEXT_PUBLIC_ variables during deployment / runtime.

Via CDK

See NextStandaloneStack construct in lib/cdk/app.ts. Or just use cli deploy command so you don't have to manage CDK yourself. See CLI help command for all congiruation, notably, it's possible to set Timeout and Memory for lambda from CLI. It is advised to always use custom --stackName in deploy command as it will affect names of all resources and will help you distinguish between different environments/applications.

If you want to use it programatically, see this guide.

Packaging

In order to succefully deploy, you firstly need to include output: 'standalone' in your next.config.js setup. Secondly, any compression should be turned off as AWS is taking care of that. See compress: false in your config. Make sure to use NextJS in version 12 or above so this is properly supported.

Once output is set, you can go on and use your next build command as you normally would. To package everything, make sure to be in your project root folder and next folder .next and public exist. Packaging is done via NPM CLI command of @slack/nextjs-lambda pack.

It will create next.out/ folder with 3 zip packages. One zip Lambda's code, one is dependencies layer and one is assets layer.

Server handler

Custom wrapper around NextServer to allow for passing ApiGateway events into Next server.

Cloudfront paths used:

Static assets

Next uses multiple directories to determine which file should be served. By default next provides us with list of routes for API/images/assets/pages. To simplify the process as much as possible, we are tapping into resulting paths.

We are packaging those assets to simulate output structure and we are using S3 behind CloudFront to serve those files. Also, Image Handler is tapping into S3 to provide images, so correct folder structure is crucial.

Cloudfront paths used:

Keep in minda, Cloudfront does not allow for multiple regex patterns in single origin, so using extensions to distinguish image/server handlers is not doable.