Deploy static Next.js website with CDK and Amplify
Tags: aws, cdk, amplify, nextjs
Published: 2021-05-17 15:38:54


In this post we will walk through deploying a Next.js starter website using AWS amplify CDK module.


Let's start with architectural overview of the application that we will be deploying.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1vwimonjw0i27ryraqlo.png)

We will be creating CodeCommit repository, Amplify app, API gateway and Lambda – all of this will be defined and deployed using AWS CDK. 

CDK code will be converted to a CloudFormation template which will be deployed in the target region to create our resources. 
We will be adding API gateway REST API that will enable communication between our Next.js app and Lambda.
We will also configure Amplify application to connect to newly created CodeCommit repository and this will trigger Amplify continuous deployment pipeline, in the end Amplify will generate a URL for our Next.js application, which will make our application accessible on the internet.

Before we start make sure that you have installed [AWS CLI] (https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html) and [Node.js] (https://nodejs.org/en/).


##### Creating Next.js app

We will start by creating a default next.js app by running:
`npx create-next-app next-cdk-app`.

This will create a basic template for our Next.js application. 
Before we start with CDK, we need to make few changes in our newly created Next.js app directory.

We will start by adding amplify.yml to the root of our directory to configure our build settings.

```yml
version: 1
frontend:
   phases:
     preBuild:
       commands:
         - npm ci
     build:
       commands:
         - npm run build
   artifacts:
     baseDirectory: out
     files:
       - '**/*'
   cache:
     paths:
       - node_modules/**/*
```

Next we need to update package.json file and modify build script by adding `next export`, which will enable fully static build of our Next.jsapplication. Our scripts should now look like this:

```json
"scripts": {
  "dev": "next dev",
  "build": "next build && next export",
  "start": "next start"
}
```

##### Initialising CDK app


We will start by creating a new directory for the CDK infrastructure within our Next.js app:

```bash
mkdir cdk-infra
cd cdk-infra
```
In this example project we will be using Typescript, however you can also use Python, JavaScript, Java, C# or Go to define your infrastructure using CDK.

Before we initialise CDK app, lets make sure that we have the latest AWS CDK Toolkit installed by running `npm install -g aws-cdk`. You can find more information about it over [here] (https://docs.aws.amazon.com/cdk/latest/guide/cli.html)

Now lets initialise our CDK application by running:

```bash
cdk init app --language typescript
```

This will create several directories necessary for CDK project in typescript to run. Our project structure should look like this:

![Screenshot 2021-06-05 at 17.35.41](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fo6wkq5jpeha0f2nfgyj.png)
 

••cdk-infra-stack.ts•• file in ••lib•• directory is where we will be defining our stack and adding resources. 

This is how your cdk-infra-stack.ts should look like:

```javascript
import * as cdk from '@aws-cdk/core';

export class CdkInfraStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // The code that defines your stack goes here
  }
}
```

Next we will start a typescript compiler by writing `npm run watch` in the command line. This will monitor our project directory for any changes and it will compile any changes to our ts files to js.

##### Adding resources

In our project we will be creating Lambda function, API Gateway, CodeCommit repository and Amplify app. in order to do that, we need to install CDK modules for each one of them. We can do that by running:

```bash
npm install @aws-cdk/aws-codecommit
@aws-cdk/aws-lambda@aws-cdk/aws-apigateway
@aws-cdk/aws-amplify
```

First we will add CodeCommit repository, that will later on connect to our Amplify app which will set up our continuous deployment with Amplify. If you prefer to connect your project to Github you can skip this step, you will still be able to set up continuous deployment with Amplify.


```javascript
import * as cdk from '@aws-cdk/core';
npm install @aws-cdk/aws-codecommit

export class CdkInfraStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);


const amplifyNextAppRepository = new codecommit.Repository(this, "AmplifyNextRepo", {
      repositoryName: "amplifyNextAppRepo",
      description: " Next.js app repository that will be used as a source repository for amplify-cdk app"
    }
    )

  }
}
```


Next we will add simple Lambda function and Api Gateway to establish a communication between the front end of our application and Lambda function. Lambda function code will be added in the Lambda directory in our cdk-infra directory

First lets add those two resources to our stack in ••cdk-infra-stack.ts•• file:

```javascript
 const helloCDK = new lambda.Function(this, "HelloCDKHandler", {
      runtime: lambda.Runtime.NODEJS_12_X,
      code: lambda.Code.fromAsset("lambda"),
      handler: "hellocdk.handler",
    });

new apigw.LambdaRestApi(this, "Endpoint", {
      handler: helloCDK,
    });
```

Here we are defining a Lambda function, that will be loaded from 'lambda' directory, from hellocdk.js file, with a 'handler' function.

Now lets add our Lambda code. First we will create new lambda directory in CDK-Infra directory, next create new file called hellocdk.js and add this simple lambda that will be returning a 'Hello from CDK!" message.

```javascript
exports.handler = async (event) => {
    const response = {
        headers: {'Access-Control-Allow-Origin': '*',
              'Access-Control-Allow-Credentials': true,
              "Access-Control-Allow-Headers": 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'},
        statusCode: 200,
        body: JSON.stringify('Hello from CDK!')
    };
    return response;
  }
```

Next we will add Amplify app, and set the source code provider to be a CodeCommit repository that have created earlier:

```javascript
const amplifyApp = new amplify.App(this, "amplifyNextApp", {
      sourceCodeProvider: new amplify.CodeCommitSourceCodeProvider({
        repository: amplifyNextAppRepo,
      }),
    });
    amplifyApp.addBranch("main");
```

In this code we are creating new Amplify application and connecting it to AmplifyAppNextRepository. We are also adding main branch - any changes pushed to that repository will trigger a new build and deployment in Amplify.
If you would like to set source code provider to be a GitHub repository you can do so by replacing CodeCommit source code with:

```javascript
sourceCodeProvider: new amplify.GitHubSourceCodeProvider({
        owner: "[Repository-Owner]",
        repository: "[RepositoryName]",
        oauthToken: cdk.SecretValue.secretsManager("[SecretName]", {
          jsonField: "[SecretKey]",
        }),
```

You can read more about source code provider class and constructs [over here](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-amplify.GitHubSourceCodeProvider.html)

##### Deployment

Now that we have defined our infrastructure using CDK, lets create/synthesize CloudFormation template by running `cdk synth` - this will generate and print the CloudFormation template into our CLI. 

Next we will deploy our application by running `cdk deploy`.
After running this, first we will see a warning message and a list of resources that will be created on our behalf. After agreeing for those changes to be made, the stack will be deployed and we should see the output with API gateway endpoint url in the command line along with stack ARN. 

Let's copy the endpoint url and add it to our index.js in Next.js ••pages•• directory. 


```javascript
import Head from "next/head";
import { useState, useEffect } from "react";
import styles from "../styles/Home.module.css";

export default function Home() {
  const [message, setMessage] = useState("Something is not working!");

  useEffect(() => {
    const url = process.env.ENDPOINT_URL
    fetch(url)
      .then((response) => response.json())
      .then((data) => setMessage(JSON.stringify(data)));
  }, []);

  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App with CDK</title>
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js </a>with{" "}
          <a href="https://docs.aws.amazon.com/cdk/latest/guide/home.html">CDK!</a>
        </h1>
        <h1>{message}</h1>
        <div className={styles.grid}></div>
      </main>

      
      </footer>
    </div>
  );
}

```

Here we have added useEffect hook that will fetch the data from the lambda function after our page is rendered. We will use this to test the functionality of the Lambda function and API gateway. The message should fist say "Something is not working", and after render, the message 'Hello from CDK!" should display. It should look like this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zszl6tiwijvxr146lsq1.png)


##### Setting up continuous deployment

Last step will be initialising git repository in the root of our next-cdk-app, connecting it to CodeCommit repository that we have just created (or GitHub repository if you have created that instead) and pushing all the changes. This will trigger continuous build and deployment. 

After pushing the changes to the repository, if we now go to AWS console, in Amplify we should now see our app built and deployed:

![Screenshot 2021-06-06 at 15.18.52](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3oihxkugx1b7wq94c422.png)
 
[Here](https://main.d341hvijq7meom.amplifyapp.com/) you can find how application should look like (with some handy links to CDK learning resources). You can also find source code in my [GitHub repository](https://github.com/ripleycmd/next-with-cdk)


------------------------------------------------------------

This blog post is based on the [CDK Day](https://www.cdkday.com/) talk I gave in April 2021, where I was presenting 2 ways of deploying React applications using CDK. You can find the talk over [here.](https://www.youtube.com/watch?v=rbiGbFvwEjI)