Using Typescript and Esbuild to deploy a Lambda Function with AWS CDK

In this previous post, we saw how to deploy a Lambda function using the CDK. The source of the Lambda was pointing to an index.js file with the following code:
exports.handler = async function(event) {
return {
statusCode: 200,
headers: { "Content-Type": "text/json" },
body: JSON.stringify({ message: "Hello from my Lambda node!" })
};
};
As we can see, the code is a simple "hello world" and far from a real-world application. Indeed in an actual application, it is more complex since you add external dependencies, use Typescript, add images, etc. hence, we might wonder:
- How to deploy a lambda function that relies on NPM modules?
- How to deploy when we use NPM modules and Typescript?
These are the two questions we are going to answer in this tutorial.
A CDK construct called AWS Lambda Nodejs helps us achieve our goal. When running the command cdk deploy
, it will first build our project with all the required dependencies then deploy the output in the cloud.
Docker and ESbuild are used under the hood to build the application:
- Docker: As you might guess, it requires having Docker installed on the computer where the build will be done.
- Esbuild: It is an extremely fast JavaScript bundler and minifier that supports Typescript files.
If the CDK detects Esbuild on the computer during the deployment, it will build the source files using it.
Prerequisites
To continue this tutorial, make sure you have the following tools installed on your computer:
- An AWS account to deploy our Lambda function
- AWS CLI configured (check out this link to see how to do it)
- Node.js 12+ with NPM or Yarn
- Docker to test the Lambda locally using AWS SAM CLI
- AWS CDK installed locally:
npm install -g cdk
What we will build
We will build a Lambda function that receives a country then returns the current time for this latter. It will be enough to show how to apply it to a real-world application.
Create the CDK project
Create a folder, then initialize an AWS CDK Typescript project:
mkdir node-lambda-timeviewer
cd node-lambda-timeviewer
cdk init sample-app --language typescript
Once installed, let's install the CDK module to build Lambda function for Node.js and Esbuild:
yarn add @aws-cdk/aws-lambda-nodejs
yarn add -D esbuild
# or
npm install @aws-cdk/aws-lambda-nodejs
npm install --save-dev esbuild
Update the content of the lib/node-lambda-timeviewer-stack.ts
with the code below:
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda-nodejs';
export class NodeLambdaTimeviewerStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new lambda.NodejsFunction(this, 'TimeViewer', {
entry: '../src/time-viewer.ts', // accepts .js, .jsx, .ts and .tsx files
functionName: 'timeViewer',
handler: 'handler',
memorySize: 512,
timeout: cdk.Duration.seconds(10)
});
}
}
The code above creates a Lambda function with some configuration options. The most important here is the "entry" properties; the value is the file's path containing the Lambda function handler.
We will write the logic of the Lambda function inside this file.
Remove the unused dependencies that come with the default project:
yarn remove @aws-cdk/aws-sns @aws-cdk/aws-sns-subscriptions @aws-cdk/aws-sqs
Write the logic of the Lambda function
We will install two packages:
- date-fns and date-fns-tz to handle date and timezone.
- tzdb for getting the timezone from a country's name.
We will also install types definition for AWS Lambda.
yarn add date-fns date-fns-tz @vvo/tzdb
yarn add -D @types/aws-lambda
At root project directory, create a folder named src
then, add the file time-viewer.ts
mkdir src
cd src
touch time-viewer.ts
Open the file and add the code below:
import { APIGatewayProxyHandler } from 'aws-lambda';
import { getTimeZones } from "@vvo/tzdb";
import { utcToZonedTime, format } from 'date-fns-tz';
export const handler: APIGatewayProxyHandler = async (event) => {
const country = 'France'
const timeZone = getTimeZones().find((timeZone) => timeZone.countryName === country);
if (!timeZone) {
return {
statusCode: 400,
headers: { "Content-Type": "text/json" },
body: JSON.stringify({ message: "Timezone not found for this country!" })
};
}
const zonedDate = utcToZonedTime(new Date(), timeZone.name)
const pattern = 'dd-MM-yyyy HH:mm:ss.SSS \'GMT\' XXX (z)'
const output = format(zonedDate, pattern, { timeZone: timeZone.name })
return {
statusCode: 200,
headers: { "Content-Type": "text/json" },
body: JSON.stringify({ time: output })
};
};
To test the code locally, we need a tool that makes the interaction between AWS CDK and AWS SAM CLI possible. Check out this post to see how to install it.
Below is the code to execute our Lambda function locally:
sam-beta-cdk local invoke NodeLambdaTimeviewerStack/TimeViewer
We got the following output:

There are two things to highlight here:
1- Green rectangle: Docker pulls the image for local SAM CLI emulation from the AWS public ECR repository.
2- Red rectangle: After the execution, we see the output returned by the Lambda function, which is the current time in France when I write this post.
Configure Esbuild options
When creating your Lambda, you can define options to use Esbuild to compile your source code. Let's update our Lambda to add some options:
export class NodeLambdaTimeviewerStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new lambda.NodejsFunction(this, 'TimeViewer', {
entry: path.resolve(__dirname, '../src/time-viewer.ts'), // accepts .js, .jsx, .ts and .tsx files
functionName: 'timeViewer',
handler: 'handler',
memorySize: 512,
timeout: cdk.Duration.seconds(10),
bundling: {
minify: true, // minify code, defaults to false
sourceMap: true, // include source map, defaults to false
sourceMapMode: lambda.SourceMapMode.INLINE, // defaults to SourceMapMode.DEFAULT
sourcesContent: false, // do not include original source into source map, defaults to true
target: 'es2020', // target environment for the generated JavaScript code
define: { // Replace strings during build time
'process.env.COUNTRY': JSON.stringify('France'),
},
}
});
}
}
Update the file time-viewer.ts to read the country from the environment variable
change: const country = 'France';
to: const country = process.env.COUNTRY;
Execute the Lambda function again to see the result:

Deploy in production
The command to deploy in production is still the same so, just do :
cdk synth
cdk deploy
Wait for the deployment to complete and connect to AWS console, go to the Lambda page, and execute your Lambda function:

Wrap up
With the possibility to build a sources project before deploying them in production, The Lambda CDK construct for Node.js enforce the integration of CDK in the developer experience. There is no need to have your project apart of the CDK project and write a bash script for the deployment. The command cdk deploy
doesn't everything for you.
You can find the code source on the GitHub repository.
Follow me on Twitter or subscribe to my newsletter to not miss the upcoming posts and the tips and tricks I share every week.
Happy to see you soon ?