open-next-cdk 0.0.10
Deploy a NextJS app using OpenNext packaging to serverless AWS using CDK
License: Apache-2.0
Requires Python: ~=3.7

<h1 align="center">
  <div align="center">
      <img align="middle" alt="Typescript" src="./resources/typescript.svg" width=15>
      <img align="middle" alt="Java" src="./resources/java.svg" width=20>
      <img align="middle" alt="Go" src="./resources/go.svg" width=30>
      <img align="middle" alt="Python" src="./resources/python.svg" width=15>
      <img align="middle" alt=".NET" src="./resources/dotnet.svg" width=30>
  </div>
  OpenNext CDK
</h1>
<div align="center">
  <a href="https://github.com/datasprayio/open-next-cdk/actions?query=workflow%3A%22build%22">
    <img align="middle" alt="Build Status" src="https://img.shields.io/github/actions/workflow/status/datasprayio/open-next-cdk/build.yml?style=for-the-badge">
  </a>
  <a href="https://github.com/datasprayio/open-next-cdk/blob/master/LICENSE">
    <img align="middle" alt="License" src="https://img.shields.io/github/license/datasprayio/open-next-cdk?style=for-the-badge">
  </a>
  <a href="https://www.npmjs.com/package/open-next-cdk">
    <img align="middle" alt="NPM release" src="https://img.shields.io/npm/v/open-next-cdk?label=RELEASE&color=blue&style=for-the-badge">
  </a>
</div>
<h3 align="center">Deploy NextJS on AWS using CDK IaC and OpenNext packaging</h3>

### Contents

* [What is this?](#what-is-this)
* [Quickstart](#quickstart)
* [Requirements](#requirements)
* [Advanced](#advanced)

  * [Pre-built OpenNext package](#pre-built-opennext-package)
  * [Additional security](#additional-security)
* [About](#about)

  * [Benefits](#benefits)
  * [Dependencies](#dependencies)
  * [Similar projects](#similar-projects)

    * [Fork from cdk-nextjs](#fork-from-cdk-nextjs)
* [Contributing](#contributing)

  * [Using Projen](#using-projen)

# What is this?

A building block for Amazon's infrastructure-as-code CDK toolkit to deploy a NextJS app using AWS serverless services.

Your NextJS app is packaged using OpenNext to fit the serverless format on Lambda

# Requirements

NextJs versions: >=12.3.0+ (includes 13.0.0+)

Platforms: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-arm64, win32-x64

# Quickstart

### NextJS setup

Add a dev dependency `esbuild@0.17.16` to your NextJS project.

```shell
npm install --save-dev esbuild@0.17.16
```

### CDK Construct

Use this construct in your CDK application to deploy your NextJS app to AWS.

<details>
  <summary>Typescript <img align="middle" alt="Typescript" src="./resources/typescript.svg" width=20></summary>  <a href="https://www.npmjs.com/package/open-next-cdk">
    <img align="middle" alt="NPM release" src="https://img.shields.io/npm/v/open-next-cdk?style=for-the-badge">
  </a>

Install the dependency using npm:

```shell
npm install --save-dev esbuild@0.17.16 open-next-cdk
```

Use the construct in your CDK application:

```python
import { Nextjs } from 'open-next-cdk';

new Nextjs(this, 'Web', {
  nextjsPath: './web', // relative path to nextjs project root
});
```

</details>
<details>
  <summary>Java <img align="middle" alt="Java" src="./resources/java.svg" width=20></summary>
  <a href="https://search.maven.org/artifact/io.dataspray/open-next-cdk">
    <img align="middle" alt="Maven Central release" src="https://img.shields.io/maven-central/v/io.dataspray/open-next-cdk?style=for-the-badge">
  </a>

Install the dependency using Maven:

```xml
<dependency>
  <groupId>io.dataspray</groupId>
  <artifactId>open-next-cdk</artifactId>
  <version>x.y.z</version>
</dependency>
```

Use the construct in your CDK application:

```java
Nextjs.Builder.create(this, getConstructId())
        .nextjsPath("./web")
        .build();
```

</details>
<details>
  <summary>Go <img align="middle" alt="Go" src="./resources/go.svg" width=20></summary>  <a href="https://github.com/datasprayio/open-next-cdk/tree/main/opennextcdk">
    <img align="middle" alt="Go release" src="https://img.shields.io/github/go-mod/go-version/datasprayio/open-next-cdk/go?filename=opennextcdk%2Fgo.mod&label=GO&style=for-the-badge">
  </a>

Install the dependency:

```shell
go get github.com:datasprayio/open-next-cdk.git@go
```

Or checkout [the code in the `go` branch](https://github.com/datasprayio/open-next-cdk/tree/go).

</details>
<details>
  <summary>Python <img align="middle" alt="Python" src="./resources/python.svg" width=20></summary>  <a href="https://pypi.org/project/open-next-cdk/">
    <img align="middle" alt="Pypi release" src="https://img.shields.io/pypi/v/open-next-cdk?style=for-the-badge">
  </a>

Install the dependency:

```shell
pip install open-next-cdk
```

</details>
<details>
  <summary>.NET <img align="middle" alt=".NET" src="./resources/dotnet.svg" width=20></summary>  <a href="https://www.nuget.org/packages/Dataspray.OpenNextCdk">
    <img align="middle" alt="Nuget release" src="https://img.shields.io/nuget/v/Dataspray.OpenNextCdk?style=for-the-badge">
  </a>

Install the dependency:

```shell
dotnet add package Dataspray.OpenNextCdk
```

</details>
<br/>

This will automatically build your NextJS app and package it for you as part of the CDK construct.

If you would prefer to package it separately, see below:

# Advanced

### Pre-built OpenNext package

<details>
  <summary>How-to</summary>

You may also provide already pre-built OpenNext package directly by building it yourself first:

```shell
open-next build
```

You will find a new folder `.open-next` which contains the packaging for your NextJS App. Now you can use the construct by instructing it not to build your app, just use the OpenNext folder directly:

```python
import { Nextjs } from 'open-next-cdk';

new Nextjs(this, 'Web', {
  openNextPath: './web/.open-next', // relative path to .open-next folder
});
```

</details>

### Additional security

<details>
  <summary>How-to</summary>

```python
import { RemovalPolicy, Stack } from "aws-cdk-lib";
import { Construct } from "constructs";
import { CfnWebAcl } from "aws-cdk-lib/aws-wafv2";
import { SecurityPolicyProtocol, type DistributionProps } from "aws-cdk-lib/aws-cloudfront";
import { Nextjs, type NextjsDistributionProps } from "cdk-nextjs-standalone";
import { Bucket, BlockPublicAccess, BucketEncryption } from "aws-cdk-lib/aws-s3";

// Because of `WebAcl`, this stack must be deployed in us-east-1. If you want
// to deploy Nextjs in another region, add WAF in separate stack deployed in us-east-1
export class UiStack {
  constructor(scope: Construct, id: string) {
    const webAcl = new CfnWebAcl(this, "WebAcl", { ... });
    new Nextjs(this, "NextSite", {
      nextjsPath: "...",
      defaults: {
        assetDeployment: {
          bucket: new Bucket(this, "NextjsAssetDeploymentBucket", {
            autoDeleteObjects: true,
            removalPolicy: RemovalPolicy.DESTROY,
            encryption: BucketEncryption.S3_MANAGED,
            enforceSSL: true,
            blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
          }),
        },
        distribution: {
          functionUrlAuthType: FunctionUrlAuthType.AWS_IAM,
          cdk: {
            distribution: {
              webAclId: webAcl.attrArn,
              minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021,
            } as DistributionProps,
          },
        } satisfies Partial<NextjsDistributionProps>,
      },
    });
  }
}
```

</details>
<br />

# About

Deploys a NextJs static site with server-side rendering and API support. Uses AWS lambda and CloudFront.

There is a new (since Next 12) [standalone output mode which uses output tracing](https://nextjs.org/docs/advanced-features/output-file-tracing) to generate a minimal server and static files.
This standalone server can be converted into a CloudFront distribution and a lambda handler that handles SSR, API, and routing.

The CloudFront default origin first checks S3 for static files and falls back to an HTTP origin using a lambda function URL.

## Benefits

This approach is most compatible with new NextJs features such as ESM configuration, [middleware](https://nextjs.org/docs/advanced-features/middleware), next-auth, and React server components ("appDir").

The unmaintained [@serverless-nextjs project](https://github.com/serverless-nextjs/serverless-next.js) uses the deprecated `serverless` NextJs build target which [prevents the use of new features](https://github.com/serverless-nextjs/serverless-next.js/pull/2478).
This construct was created to use the new `standalone` output build and newer AWS features like lambda function URLs and fallback origins.

You may want to look at [Serverless Stack](https://sst.dev) and its [NextjsSite](https://docs.sst.dev/constructs/NextjsSite) construct for an improved developer experience if you are building serverless applications on CDK.

## Dependencies

Built on top of [open-next](https://open-next.js.org/), which was partially built using the original core of cdk-nextjs-standalone.

## Similar projects

This project is heavily based on

* [Open-next](https://open-next.js.org/)
* [https://github.com/iiroj/iiro.fi/commit/bd43222032d0dbb765e1111825f64dbb5db851d9](https://github.com/iiroj/iiro.fi/commit/bd43222032d0dbb765e1111825f64dbb5db851d9)
* [https://github.com/sladg/nextjs-lambda](https://github.com/sladg/nextjs-lambda)
* [https://github.com/serverless-nextjs/serverless-next.js/tree/master/packages/compat-layers/apigw-lambda-compat](https://github.com/serverless-nextjs/serverless-next.js/tree/master/packages/compat-layers/apigw-lambda-compat)
* [Serverless Stack](https://github.com/serverless-stack/sst)

  * [RemixSite](https://github.com/serverless-stack/sst/blob/master/packages/resources/src/NextjsSite.ts) construct
  * [NextjsSite](https://github.com/serverless-stack/sst/blob/master/packages/resources/src/RemixSite.ts) construct

### Fork from cdk-nextjs

Compatible with: `cdk-nextjs`[@3.2.1](https://github.com/jetbridge/cdk-nextjs/releases/tag/v3.2.1)

This project has been initially forked from [cdk-nextjs](https://github.com/jetbridge/cdk-nextjs) in order to [publish the package to other langugages](https://github.com/jetbridge/cdk-nextjs/issues/120#issuecomment-1634926223). So far notable changes are:

* Extended language support: TS, Java, Go, .NET, Python.
* Extended platform support: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-arm64, win32-x64
* Use pre-built open-next package

# Contributing

Hey there, we value every new contribution a lot 🙏🏼 thank you. Open an issue or a PR and we'll gladly help you out.

## Using Projen

Most boilerplate files are pre-generated including package.json. Don't update it directly, rather update `.projenrc.js` then run `yarn projen` to re-generate the files.