🥇 The Lambdalith Advantage: A Complete Guide to NestJS Deployment on AWS Lambda Using CDK
Valentin BEGGI
Posted on November 14, 2023
TL;DR 📚
- Why is a Lambdalith approach a high ROI choice for many? 💸
- Learn how to deploy a monolithic NestJS app on AWS Lambda using Webpack and AWS CDK. 🚀
The Lambdalith Edge for NestJS on AWS Lambda 🌟
🧠 A Lambdalith is a monolithic architecture approach for serverless applications where a single AWS Lambda function serves the entire API, rather than deploying separate functions for each endpoint.
Opt for a Lambdalith and reap multiple benefits for your NestJS API:
- Faster Rollouts: Quicker deployments and streamlined management, irrespective of your number of routes.
- Minimized Cold Starts: Enhanced performance through more frequent reuse of a single Lambda function.
- Easier Logging: A single point for logs simplifies monitoring and alert setup.
- Full NestJS Benefits: Fully exploit NestJS's rich features and community support.
While a Lambdalith might mean lengthier cold starts and broader control scopes, its efficiency, simplicity, and high return on investment are unmatched.
Monorepo structure 🚧📦: I strongly advice you embrace a monorepo structure, with a package for your API and a package for your infrastructure (CDK).
Setting Up the Infrastructure with AWS CDK 🏗️
AWS CDK transforms infrastructure into code. Kick things off by installing AWS CDK and initiating a TypeScript project with cdk init app --language typescript
.
In the lib/my-stack.ts
file, begin with the core of your setup: the Lambda function.
// LambdaNestStack in stack.ts
const apiNestHandlerFunction = new Function(this, "ApiNestHandler", {
code: Code.fromAsset("api/dist"), // 👈 This is crucial
runtime: Runtime.NODEJS_18_X,
handler: "main.handler",
environment: {}, // 👈 You might need env variables
});
Next up, create a Rest API with a Lambda proxy at its root. This API Gateway acts as the traffic controller, directing all requests to your Lambda-powered NestJS app. All route paths will be directed to your single Lambda. 🗾
const api = new RestApi(this, "Api", {
deploy: true,
defaultMethodOptions: {
apiKeyRequired: true,
},
});
api.root.addProxy({
defaultIntegration: new LambdaIntegration(apiNestHandlerFunction, { proxy: true }),
});
const apiKey = api.addApiKey("ApiKey"); // 👈 to ease your testing
const usagePlan = api.addUsagePlan("UsagePlan", {
name: "UsagePlan",
apiStages: [
{
api,
stage: api.deploymentStage,
},
],
});
usagePlan.addApiKey(apiKey);
In this snippet, Code.fromAsset("api/dist")
is crucial. It points to the location of our bundled NestJS app, ensuring efficient Lambda execution.
Prepping the NestJS App for Lambda 🦁
Start by creating a new NestJS app with nest new api
. Then, install the @nestjs/platform-express
and @vendia/serverless-express
packages.
You now have a classic NestJS app, ready to be adapted for AWS Lambda.
Next to the main.ts
file, create a new lambda.ts
file. This file will be the entry point of our Lambda function.
// lambda.ts
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import serverlessExpress from '@vendia/serverless-express';
import { Context, Handler } from 'aws-lambda';
import express from 'express';
import { AppModule } from './app.module';
let cachedServer: Handler;
async function bootstrap() {
if (!cachedServer) {
const expressApp = express();
const nestApp = await NestFactory.create(
AppModule,
new ExpressAdapter(expressApp),
);
nestApp.enableCors();
await nestApp.init();
cachedServer = serverlessExpress({ app: expressApp });
}
return cachedServer;
}
const handler = async (event: any, context: Context, callback: any) => {
const server = await bootstrap();
return server(event, context, callback);
};
module.exports.handler = handler;
This code will be executed by AWS Lambda. It creates a NestJS app and adapts it to the AWS Lambda environment. It also ensures that the NestJS app is only created once, improving performance. ⚡
🗒️ Side Note: You could easily setup a single
main.ts
entry point by leveraging env variables to deduce the execution context: Lambda or local
Now, we need to bundle this TypeScript code into a single file...🤓
Packing it Up with Webpack 📦🧙♂️
There are several ways to bundle a NestJS app for AWS Lambda. You could use Lambda Layers, but this is not the most efficient approach. Instead, we'll use Webpack to bundle our NestJS app into a single file, which we'll then deploy with AWS CDK.
Let's start by creating a new webpack.config.js
file in our API package. This file will define our Webpack configuration.
module.exports = function (options, webpack) {
return {
...options,
entry: ['./src/lambda.ts'],
externals: [],
output: {
...options.output,
libraryTarget: 'commonjs2',
},
plugins: [
...options.plugins,
new webpack.IgnorePlugin({
checkResource(resource) {
// Ignoring non-essential modules for Lambda deployment
return lazyImports.includes(resource);
},
}),
],
};
};
This configuration bundles our Lambda entry file (lambda.ts
) and its dependencies, creating a lean and efficient package for AWS Lambda!
Make sure to create a build-lambda script in your package.json
file!
{
"scripts": {
"build-lambda": "nest build --webpack --webpackPath webpack.config.js"
}
}
Deploying the NestJS App: To the Cloud! ☁️
Your NestJS app is now a compact bundle, thanks to Webpack. Deploying? It's as simple as:
-
Build: Run
npm run build-lambda
in your API package. -
Deploy: In your infrastructure package, execute
cdk deploy
.
And like that, your NestJS app ascends to AWS Lambda, primed for action. 💫
Your High-Performance NestJS App now lives on AWS 🚀
Congratulations! You've unlocked the strategy for a potent, scalable, and efficient NestJS app on AWS Lambda, all packaged neatly with Webpack and AWS CDK. 👏👏
Please feel free to comment if anything was unclear or if you found a better way to achieve this result! 💬
Posted on November 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
January 16, 2024
November 14, 2023