# NextJS Lambda Utils

This is a set of utils needed for deploying NextJS into AWS Lambda.
It includes a wrapper for `next/server/image-optimizer` allowing to use S3.
And includes CLI and custom server handler to integrate with ApiGw.

- [NextJS Lambda Utils](#nextjs-lambda-utils)
  - [Usage](#usage)
  - [Sharp](#sharp)
  - [Packaging](#packaging)
    - [Server handler](#server-handler)
    - [Static assets](#static-assets)
- [TODO](#todo)

## Usage

Create new lambda function with NODE_16 runtime in AWS.
Use

```
const sharpLayer: LayerVersion
const assetsBucket: Bucket

const code = require.resolve('@sladg/nextjs-lambda/image-handler/zip')

const imageOptimizerFn = new Function(this, 'LambdaFunction', {
      code: Code.fromAsset(code),
      runtime: Runtime.NODEJS_16_X,
      handler: 'index.handler',
      layers: [sharpLayer],
      memorySize: 1024,
      timeout: Duration.seconds(15),
      environment: {
        S3_BUCKET_SOURCE: assetsBucket.bucketName
      }
    })
```

Lambda consumes Api Gateway 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.

## Sharp

Besides handler (wrapper) itself, underlying NextJS also requires sharp binaries.
To build those, we use `npm install` with some extra parametes. Then we zip all sharp dependencies and compress it to easily importable zip file.

```
const code = require.resolve('@sladg/nextjs-lambda/sharp-layer')

const sharpLayer = new LayerVersion(this, 'SharpLayer', {
  code: Code.fromAsset(code)
})
```

## Packaging

In order to succefully deploy, you firstly need to include `target: 'standalone'` in your `next.config.js` setup.
Make sure to use NextJS in version 12 or above so this is properly supported.

Once target 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 package`.

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

- code zip: include all files generated by next that are required to run on Lambda behind ApiGateway. Original handler as well as new server handler are included. Use `handler.handler` for custom one or `server.handler` for original one.
- dependencies layer: all transpilied `node_modules`. Next includes only used files, dramatically reducing overall size.
- assets layer: your public folder together with generated assets. Keep in mind that to public refer file, you need to include it in `public/assets/` folder, not just in public. This limitation dramatically simplifies whole setup. This zip file is uploaded to S3, it's not included in Lambda code.

### Server handler

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

Cloudfront paths used:

- `default`
- `_next/data/*`

### 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:

- `_next/*`
- `assets/*`

# TODO

- Explain script used for packaging Next app,
- Add CDK examples on how to set it up,
- Export CDK contruct for simple plug-n-play use,
- Use lib/index.ts as single entry and export all paths/functions from it (including zip paths).
