In this guide, we'll dive into the process of deploying a Next.js application using AWS CDK (Cloud Development Kit) and Amplify Gen 2. We'll explore how these powerful tools can be combined to create a seamless, serverless deployment pipeline for your Next.js projects. You'll learn how to leverage AWS CDK's infrastructure-as-code capabilities to define your cloud resources, while harnessing Amplify's built-in support for Next.js to simplify hosting and continuous deployment. By the end of this journey, you'll have a robust understanding of how to create scalable, easily maintainable Next.js deployments on AWS.
Original Post
In this comprehensive guide, we'll explore the process of deploying a Next.js application using AWS CDK (Cloud Development Kit) and Amplify Gen 2. We'll demonstrate how these tools can be combined to create a seamless, serverless deployment pipeline for your Next.js projects. You'll learn to harness AWS CDK's infrastructure-as-code capabilities to define and manage your cloud resources efficiently, while leveraging Amplify's specialized support for Next.js to streamline hosting and enable continuous deployment. By the end of this tutorial, you'll have gained a robust understanding of how to deploy your Next.js app to AWS.
Here is the github repo
AWS Amplify
AWS Amplify is a comprehensive service designed to simplify full-stack application development and deployment on AWS. It provides a unified, streamlined developer experience by abstracting away the complexity of managing multiple AWS services. Amplify leverages a suite of powerful AWS tools including S3, API Gateway, CodeBuild, Cognito, and Lambda, enabling developers to create robust infrastructure supporting their required features without needing deep expertise in cloud architecture.
One of Amplify's key strengths is its dedicated UI libraries for popular frameworks such as React, Next.js, Angular, and Vue, facilitating faster integration and development. The core concept behind Amplify is elegantly simple: using only TypeScript code, developers can express their app's data model, business logic, authentication, and authorization rules. Amplify then automatically configures the appropriate cloud resources, eliminating the need to manually stitch together underlying AWS services. This approach positions Amplify as a comprehensive, end-to-end full-stack development framework.
Amplify Gen 2 represents AWS's latest effort to further streamline the development process for full-stack applications. It builds upon the foundations of the original Amplify, offering enhanced features and improved performance. This new generation aims to make it even easier for developers to harness the power of AWS services, reducing the learning curve and allowing teams to focus more on building innovative applications rather than managing infrastructure. With Amplify Gen 2, AWS continues to bridge the gap between development and operations, empowering developers to create sophisticated, scalable applications with greater efficiency and less complexity.
Topics Covered in this guide
In this tutorial, we'll walk through the process of deploying and updating a Next.js application using AWS CDK and Amplify. We'll cover the entire workflow from initial development to automated deployment. Here's what we'll accomplish:
- Create a Next.js application: We'll start by building a starter Next.js app that utilizes both Server-Side Rendering (SSR) and Static Site Generation (SSG) capabilities.
- Version control with GitHub: We'll push our frontend code to a GitHub repository, setting the stage for continuous integration and deployment.
- AWS CDK and Amplify setup: Using AWS CDK and Amplify libraries, we'll create an Amplify project that specifies the build steps and connects to our GitHub repository. This step will demonstrate how to define your cloud infrastructure as code.
- Initial deployment: We'll trigger an initial deployment to test our setup and ensure everything is working correctly.
- Implement and deploy changes: Finally, we'll make updates to our frontend, push these changes to GitHub, and observe how Amplify automatically detects and deploys these updates, showcasing the power of continuous deployment.
Prerequisites
- An AWS account
- Node.js and npm installed
- AWS cli and cdk setup and configured
- Basic knowledge in Javascript and Typescript
Github Project
Open up Github, create a new repo and name it nextjs-aws-amplify
. See this repo as an example:
https://github.com/ababakanian/nextjs-aws-amplify
Pull the code locally (replace the username with yours)
git clone git@github.com:<your-user-name>/nextjs-aws-amplify.git
Get an access token
In addition, lets create an access token in github. Go to the Github Personal access token page page and click on Generate new token (classic)
select a name for the access token and from teh select scopes list select the repo option. Scroll down and click on Generate token.
Copy the generate token and lets add it to an env file.
cd nextjs-aws-amplify
touch .env
open the .env file and add a variable for the github token in the env file.
So your .env file should look this this.
GITHUB_ACCESS_TOKEN=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
Note: Do no copy this value, this is not going to work for you as it belongs to our repo and also it's been deleted.
Add the .env file to your .gitignore list, touch .env
and then open the file and add .env.
Create a Next.js Project
Make sure you are in the nextjs-aws-amplify
folder, then create a NextJS project. Open a terminal and type:
npx create-next-app@latest
enter frontend
for the app name. Then you will be presented with several options, choose the options as you
see here:
Need to install the following packages:
create-next-app@14.2.5
Ok to proceed? (y) y
✔ What is your project named? … test-frontend
✔ Would you like to use TypeScript? … No / **Yes**
✔ Would you like to use ESLint? … No / **Yes**
✔ Would you like to use Tailwind CSS? … No / **Yes**
✔ Would you like to use `src/` directory? … No / **Yes**
✔ Would you like to use App Router? (recommended) … No / **Yes**
✔ Would you like to customize the default import alias (@/*)? … **No** / Yes
We will come back and modify the frontend code later. However, lets push this code to github and get the github config info.
git add .
git commit -m "initial commit, added frontend nextjs"
git push origin main
create a .nvmrc
file and add the value nodejs.
echo "v20.11.1" > .nvmrc
Create AWS CDK
By leveraging an IaC system like AWS cdk, you can automate and replicate all the setup and configuration work typically done through the console. This approach significantly speeds up your website deployment process.
- Initialize a new CDK project, make sure you are in the top level
nextjs-aws-amplify
folder created earlier. Then create an infrastructure folder and initialize it with cdk code.
mkdir infrastructure && cd infrastructure
cdk init app --language typescript
If you haven't setup cdk yet, visit [AWS CDK Getting started (https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html)
- Install the amplify library
npm i @aws-cdk/aws-amplify-alpha@2.39.1-alpha.0 --save
Note: At the time of writing this post the library is in alpha stage. Check the npm page here to see if the version has left the alpha stage.
- Install supporting libraries:
npm i dotenv --save
- Create a file named in lib named
lib/amplify-gen-stack.ts
with the following content:
import * as amplify from "@aws-cdk/aws-amplify-alpha"
import { Construct } from "constructs"
import { CfnOutput, SecretValue, Stack, StackProps } from "aws-cdk-lib"
import { BuildSpec } from "aws-cdk-lib/aws-codebuild"
import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"
import { CfnApp, CfnBranch } from "aws-cdk-lib/aws-amplify"
export interface AmplifyStackProps extends StackProps {
githubOauthToken: SecretValue
repoOwner: string
repoName: string
domain: string
}
export class AmplifyStack extends Stack {
constructor(scope: Construct, id: string, props: AmplifyStackProps) {
super(scope, id, props)
const amplifyApp = new amplify.App(this, "AmplifyAppResource", {
appName: props.repoName,
description: "Nextjs frontend",
role: new Role(this, "AmplifyRoleWebApp", {
assumedBy: new ServicePrincipal("amplify.amazonaws.com"),
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess-Amplify"),
],
}),
sourceCodeProvider: new amplify.GitHubSourceCodeProvider({
oauthToken: props.githubOauthToken,
owner: props.repoOwner,
repository: props.repoName,
}),
buildSpec: BuildSpec.fromObjectToYaml({
version: "1.0",
applications: [
{
appRoot: "frontend",
frontend: {
phases: {
preBuild: {
commands: [
"nvm install",
"nvm use",
"export NODE_OPTIONS=--max-old-space-size=8192",
"npm install",
],
},
build: {
commands: ["npm run build"],
},
},
artifacts: {
baseDirectory: ".next",
files: ["**/*"],
},
},
},
],
}),
})
const cfnApp = amplifyApp.node.defaultChild as CfnApp
cfnApp.platform = "WEB_COMPUTE"
const mainBranch = amplifyApp.addBranch("main", {
autoBuild: true,
stage: "PRODUCTION",
})
const domain = amplifyApp.addDomain(props.domain, {
enableAutoSubdomain: true,
})
domain.mapRoot(mainBranch)
domain.mapSubDomain(mainBranch, "www")
const cfnBranch = mainBranch.node.defaultChild as CfnBranch
cfnBranch.framework = "Next.js - SSR"
new CfnOutput(this, "AmplifyAppURL", {
value: `https://main.${amplifyApp.defaultDomain}`,
description: "Amplify App URL",
})
}
}
The amplify.App
construct has two main parts, the source code repo configuration and the build instructions.
We define the code repo location using
sourceCodeProvider: new amplify.GitHubSourceCodeProvider({
oauthToken: props.githubOauthToken,
owner: props.repoOwner,
repository: props.repoName,
})
and we define the build and artifacts information:
buildSpec: BuildSpec.fromObjectToYaml({
version: "1.0",
applications: [
{
appRoot: "frontend",
frontend: {
phases: {
preBuild: {
commands: [
"nvm install",
"nvm use",
"export NODE_OPTIONS=--max-old-space-size=8192",
"npm install",
],
},
build: {
commands: ["npm run build"],
},
},
artifacts: {
baseDirectory: ".next",
files: ["**/*"],
},
},
},
],
}),
})
essentially we are installing node, and installing all the node packages in the prebuild step. Then we run npm run build
to build the nextjs app.
The artifacts section specifies where our external files will be located.
To learn more check the AWS cdk Documentation
Lets use the stack in our bin/infrastructure.ts
file:
#!/usr/bin/env node
import "source-map-support/register"
import * as cdk from "aws-cdk-lib"
import * as dotenv from "dotenv"
import { AmplifyStack } from "../lib/amplify-gen-stack"
dotenv.config({ path: "../.env" })
const {
AWS_ACCOUNT_ID,
AWS_REGION,
DOMAIN,
GITHUB_ACCESS_TOKEN,
GITHUB_REPO_OWNER,
GITHUB_REPO_NAME,
} = process.env
if (
!AWS_ACCOUNT_ID ||
!AWS_REGION ||
!GITHUB_ACCESS_TOKEN ||
!GITHUB_REPO_OWNER ||
!GITHUB_REPO_NAME ||
!DOMAIN
) {
console.error("check .env file")
throw new Error("check .env file")
}
const app = new cdk.App()
new AmplifyStack(app, "AmplifyStack", {
githubOauthToken: cdk.SecretValue.unsafePlainText(GITHUB_ACCESS_TOKEN),
repoOwner: GITHUB_REPO_OWNER,
repoName: GITHUB_REPO_NAME,
domain: DOMAIN,
env: {
account: AWS_ACCOUNT_ID,
region: AWS_REGION,
},
})
add the remaining variable to the .env
file we created earlier:
AWS_ACCOUNT_ID=AWS account number
AWS_REGION=your deployment region
DOMAIN=your domain name
GITHUB_REPO_OWNER=the github repo owner
GITHUB_REPO_NAME=the repo name
GITHUB_ACCESS_TOKEN=the github access token
so your data should look something like this;
AWS_ACCOUNT_ID="975421487117"
AWS_REGION="us-east-1"
DOMAIN=redrobotexample.com
GITHUB_ACCESS_TOKEN=ghp_KGu0GbvmdOiFNUzK6mGGFRBxBMM3rc1sTq0C
GITHUB_REPO_OWNER=redrobotdev
GITHUB_REPO_NAME=nextjs-aws-amplify
now deploy the stack, make sure you are in the infrastructure
folder then call deploy.
cd infrastructure
cdk deploy
Once deployment, you will see the following message in the console
AmplifyStack: creating CloudFormation changeset...
✅ AmplifyStack
✨ Deployment time: 51.82s
Outputs:
AmplifyStack.AmplifyAppURL = https://main.dqsf8v3g3qb5.amplifyapp.com
Stack ARN:
arn:aws:cloudformation:us-east-1:975050147627:stack/AmplifyStack/3e7e4c70-58c9-11ef-bff5-121072f7c15f
✨ Total time: 56.97s
Deploy the Application
So we have successfully created an Amplify project, we need to go to teh AWS console and start a deployment.
Go to the AWS Console page, and open the AWS Amplify
service page. You should see the nextjs-aws-amplify, project listed.
Click on the project and if you see a Migrate to Github app message, ignore it by clicking Remind me later.
Click on the main branch
Then click on the Run job button
Wait until the Deployments is done
Once deployed, the box should go green, and the status should say deployed
visit the domain name and you should see the Nextjs page.
Update the frontend
Amplify has CI/CD integrated into it's system, so we can make changes to our frontend and push our changed directly to github. Amplify will get notified of a recent push, and it will automatically pull the code, build and redeploy it.
Lets first install shadcn in our frontend. make sure you have traversed into the frontend folder cd frontend
.
npx shadcn-ui@latest init
then install the card and button components
npx shadcn-ui@latest add card button
Open the file from the same frontend folder /src/app/page.tsx
:
import Image from "next/image"
import Link from "next/link"
export default function Home() {
return (
<main className="flex min-h-screen flex-col space-y-4 items-center p-24">
<Image
className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
<Link href="/invoice" className="underline">
Invoice
</Link>
</main>
)
}
Lets create a new folder named invoice
and page.tsx
so you have the path nextjs-aws-amplify/src/app/invoice/page.tsx
. Paste the following content in there:
export const dynamic = "force-dynamic"
import React from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import Link from "next/link"
export default async function FinancePage() {
const income = Math.floor(Math.random() * 100000) + 50000 // Random income between 50,000 and 150,000
const profit = Math.floor(Math.random() * (income * 0.3)) // Random profit up to 30% of income
return (
<div className="container mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">Financial Dashboard</h1>
<Link href="/">
<Button className="mb-4">Home</Button>
</Link>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card>
<CardHeader>
<CardTitle>Income</CardTitle>
</CardHeader>
<CardContent>
<p className="text-3xl font-bold">${income.toLocaleString()}</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Profit</CardTitle>
</CardHeader>
<CardContent>
<p className="text-3xl font-bold">${profit.toLocaleString()}</p>
</CardContent>
</Card>
</div>
</div>
)
}
commit and push the changes
git add .
git commit -m "added the invoice page"
git push origin main
from the AWS console/Amplify Service page you will see a new deployment has started. Make sure the deployment goes through fine and refresh the website to see the affected changes.
Verify the changes has taken affect and you can see the invoice page.
Note: The home page is a Static page so it's not showcasing the power of amplify serverless nextjs deployment. Go to the
invoice
page and refresh the page and you will notice on each refresh the values changes. This page is Server Side Rendered and demonstrates the server side rendering capability of Next.js.
Conclusion
In this short guide, we've walked through the process of deploying a simple Next.js page using AWS CDK and Amplify Gen 2. We've covered everything from setting up a Next.js project and managing it with GitHub, to creating an AWS CDK stack for Amplify deployment, and finally implementing and automatically deploying updates. By leveraging the power of AWS CDK's infrastructure-as-code capabilities and Amplify's streamlined deployment pipeline, we've demonstrated how to create a scalable, easily maintainable Next.js application on AWS. This approach not only simplifies the deployment process but also enables continuous integration and deployment, allowing developers to focus more on building innovative features rather than managing infrastructure.
Interested in learning more?
If you are interested in creating a project from scratch using AWS CDK and serverless technologies, you can check out the course linked below. In this course, we go over each service in detail, explaining what they do, and actually create a dynamic application with user login and authentication.
Top comments (0)