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! 💬
Top comments (0)