DEV Community

Natalia Marek for AWS Community Builders

Posted on • Edited on

Deploy static Next.js website with CDK and Amplify

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

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 and Node.js.

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.

version: 1
frontend:
   phases:
     preBuild:
       commands:
         - npm ci
     build:
       commands:
         - npm run build
   artifacts:
     baseDirectory: out
     files:
       - '**/*'
   cache:
     paths:
       - node_modules/**/*
Enter fullscreen mode Exit fullscreen mode

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:

"scripts": {
  "dev": "next dev",
  "build": "next build && next export",
  "start": "next start"
}
Enter fullscreen mode Exit fullscreen mode
Initialising CDK app

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

mkdir cdk-infra
cd cdk-infra
Enter fullscreen mode Exit fullscreen mode

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

Now lets initialise our CDK application by running:

cdk init app --language typescript
Enter fullscreen mode Exit fullscreen mode

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

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

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

npm install @aws-cdk/aws-codecommit
@aws-cdk/aws-lambda@aws-cdk/aws-apigateway
@aws-cdk/aws-amplify
Enter fullscreen mode Exit fullscreen mode

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.

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"
    }
    )

  }
}
Enter fullscreen mode Exit fullscreen mode

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:

 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,
    });
Enter fullscreen mode Exit fullscreen mode

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.

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;
  }
Enter fullscreen mode Exit fullscreen mode

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

const amplifyApp = new amplify.App(this, "amplifyNextApp", {
      sourceCodeProvider: new amplify.CodeCommitSourceCodeProvider({
        repository: amplifyNextAppRepo,
      }),
    });
    amplifyApp.addBranch("main");
Enter fullscreen mode Exit fullscreen mode

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:

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

You can read more about source code provider class and constructs over here

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.

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>
  );
}

Enter fullscreen mode Exit fullscreen mode

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

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

Here 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


This blog post is based on the CDK Day 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.

Top comments (2)

Collapse
 
hoanghai31299 profile image
hoanghai31299

Heyy, great article.
Just a question, there is any way to signin with cognito credentials?
In my case, android call the webview (nextjs using amplify auth) and send credentials to it. I want to login using that credentials.

Collapse
 
firxworx profile image
firxworx

Heads up the GitHub repo is incomplete + missing files discussed in the article + the talk on YouTube. For example there is no ./cdk-infra/lambda folder in the repo.