Deploy Nest JS on AWS
Deploy your Nest JS app on AWS with Lambda and API Gateway using the AWS CDK.
There are a few ways to deploy your Nest JS app on AWS:
-
Standard Lambda and API Gateway.
-
Node.js runtime container image on Lambda.
-
Bun runtime container image on Lambda.
-
One-click installation on your AWS account using Thunder console.
View code in StackBlitz
Deploy Nest JS on AWS Lambda + API Gateway
CDK Functions is a package that simplifies the deployment of Lambdas using the AWS Cloud Development Kit (CDK). It provides a straightforward way to deploy your Nest JS app to AWS Lambda and API Gateway.
1. Create a project
You can create a new Nest JS project using the following commands:
npm create nestjs@latest my-nestjs-appcd my-nestjs-app
pnpm create nestjs my-nestjs-appcd my-nestjs-app
bun create nestjs my-nestjs-appcd my-nestjs-app
2. Initialize your project
Install the necessary dependencies and initialize your project:
npm i tsx aws-cdk-lib @thunderso/cdk-functions --save-dev
pnpm add -D tsx aws-cdk-lib @thunderso/cdk-functions
bun add -d tsx aws-cdk-lib @thunderso/cdk-functions
3. Create a CDK stack and Lambda handler
Create a stack/index.ts
file to create your CDK stack. Edit it to match your project:
import { App } from "aws-cdk-lib";import { Runtime, Architecture} from 'aws-cdk-lib/aws-lambda';import { FunctionStack, type FunctionProps } from '@thunder-so/cdk-functions';
const fnStack: FunctionProps = { env: { account: '4477996600XX', region: 'us-east-1', }, application: 'nestjs', service: 'api', environment: 'dev',
functionProps: { codeDir: 'dist', handler: 'lambda-node.handler', include: [ 'node_modules', // Include node_modules for dependencies 'lambda-node.js', // Include the Lambda handler file ], exclude: ['node_modules/.npmignore', '**/*.ts', '**/*.map'], },};
new FunctionStack(new App(), `${fnStack.application}-${fnStack.service}-${fnStack.environment}-stack`, fnStack);
Create a lambda-node.js
file in the root directory to handle the Lambda function:
const { NestFactory } = require('@nestjs/core');const { AppModule } = require('./app.module');const { configure } = require('@vendia/serverless-express');
let cachedServer;
async function bootstrapServer() { if (!cachedServer) { const app = await NestFactory.create(AppModule); await app.init(); const expressApp = app.getHttpAdapter().getInstance(); cachedServer = configure({ app: expressApp }); } return cachedServer;}
exports.handler = async (event, context) => { const server = await bootstrapServer(); return server(event, context);};
4. Deploy
Before you deploy, run your build script to generate artifacts in the dist
directory.
npm run build
pnpm run build
bun run build
By running the following script, the CDK stack will be deployed to AWS.
npx cdk deploy --all --app="npx tsx stack/index.ts"
pnpm exec cdk deploy --all --app="pnpm exec tsx stack/index.ts"
npx cdk deploy --all --app="bunx tsx stack/index.ts"
When the deployment is complete, you will see the API Gateway URL in the output. You can access your Nest JS app at that URL.
For complete documentation, refer to the CDK Functions documentation.
Node.js runtime container image on Lambda
You can also deploy your Nest JS app on AWS Lambda using a Node.js container image. This method allows you to package your application and its dependencies into a Docker image, which can then be deployed to AWS Lambda.
1. Create a lambda handler
Create a lambda-node.js
file in the root directory to handle the Lambda function:
const { NestFactory } = require('@nestjs/core');const { AppModule } = require('./app.module');const { configure } = require('@vendia/serverless-express');
let cachedServer;
async function bootstrapServer() { if (!cachedServer) { const app = await NestFactory.create(AppModule); await app.init(); const expressApp = app.getHttpAdapter().getInstance(); cachedServer = configure({ app: expressApp }); } return cachedServer;}
exports.handler = async (event, context) => { const server = await bootstrapServer(); return server(event, context);};
2. Create a Dockerfile
Create a Dockerfile.node
in the root directory to build your Nest JS app as a container image:
FROM public.ecr.aws/lambda/nodejs:22 AS baseWORKDIR ${LAMBDA_TASK_ROOT}
# Copy dist files and install dependenciesCOPY . .RUN npm install --omit=dev
# Set the Lambda handlerCMD [ "lambda-node.handler" ]
Create/modify your CDK stack
Create a stack/node.ts
file (or modify your existing stack/index.ts
) to define your CDK stack:
const fnStack: FunctionProps = { // ... other props
functionProps: { codeDir: 'dist', include: [ 'package.json', // Include package.json for dependencies 'lambda-node.js' // Include the Lambda handler file ], dockerFile: 'Dockerfile.node', },};
By running the following script, the CDK stack will be deployed to AWS.
npx cdk deploy --all --app="npx tsx stack/node.ts"
pnpm exec cdk deploy --all --app="pnpm exec tsx stack/node.ts"
npx cdk deploy --all --app="bunx tsx stack/node.ts"
Bun runtime container image on Lambda
You can also deploy your Nest JS app on AWS Lambda with custom Bun runtime.
1. Create a lambda handler
Create a lambda-bun.js
file in the root directory to handle the Lambda function:
const { NestFactory } = require('@nestjs/core');const { AppModule } = require('./app.module');
let app, expressApp;
async function getExpressApp() { if (!expressApp) { app = await NestFactory.create(AppModule); await app.init(); expressApp = app.getHttpAdapter().getInstance(); } return expressApp;}
exports.fetch = async (request /*: Request*/, server /*: Server*/) => { const expressApp = await getExpressApp();
// Convert Bun's Request to Node.js req/res for Express // Use 'node-fetch' Response to Express bridge const { Readable } = require('stream'); const { Headers } = require('node-fetch');
// Create a mock req object const url = new URL(request.url); const req = new Readable(); req.url = url.pathname + url.search; req.method = request.method; req.headers = Object.fromEntries(request.headers.entries()); req._read = () => {}; if (request.body) { const body = Buffer.from(await request.arrayBuffer()); req.push(body); } req.push(null);
// Create a mock res object return new Promise((resolve) => { const chunks = []; const res = { statusCode: 200, headers: {}, setHeader(key, value) { this.headers[key] = value; }, getHeader(key) { return this.headers[key]; }, write(chunk) { chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); }, end(chunk) { if (chunk) this.write(chunk); const body = Buffer.concat(chunks); resolve( new Response(body, { status: res.statusCode, headers: res.headers, }) ); }, status(code) { this.statusCode = code; return this; }, send(body) { this.end(body); }, }; expressApp(req, res); });};
2. Create a Dockerfile
Create a Dockerfile.bun
in the root directory to build your Nest JS app as a container image with Bun runtime:
# Builder imageFROM oven/bun:latest AS bunWORKDIR /tmp
## Install the bun dependenciesRUN apt-get update && apt-get install -y curlRUN curl -fsSL https://raw.githubusercontent.com/oven-sh/bun/main/packages/bun-lambda/runtime.ts -o /tmp/runtime.tsRUN bun install aws4fetchRUN bun build --compile runtime.ts --outfile bootstrap
# Runtime imageFROM public.ecr.aws/lambda/provided:al2023WORKDIR ${LAMBDA_TASK_ROOT}
## Copy bun + bootstrap from the builder imageCOPY --from=bun /usr/local/bin/bun /opt/bunCOPY --from=bun /tmp/bootstrap ${LAMBDA_RUNTIME_DIR}
## Copy dist files and install dependenciesCOPY . .RUN /opt/bun install --frozen-lockfile
# Set our handler methodCMD [ "lambda-bun.fetch" ]
3. Create/modify your CDK stack
Create a stack/bun.ts
file to define your CDK stack:
const fnStack: FunctionProps = { // ... other props
functionProps: { codeDir: 'dist', include: [ 'package.json', // Include package.json for dependencies 'bun.lock', // Include bun.lock for Bun dependencies 'lambda-bun.js' // Include the Lambda handler file ], dockerFile: 'Dockerfile.bun', },};
By running the following script, the CDK stack will be deployed to AWS.
npx cdk deploy --all --app="bunx tsx stack/bun.ts"