Deploy a Lambda Function in Node.js with AWS CDK v2

Photo by NASA / Unsplash
Photo by NASA / Unsplash

The AWS CDK is a tool that makes it easy to create your infrastructure through the code. Before, we used CloudFormation or the AWS console to create our infrastructure. Using these paths has some drawbacks:

  • The CloudFormation is hard to read and navigate.
  • There is no way to use computed or dynamic expressions.
  • When using the console, you don't have the history of our changes on the infrastructure.
  • It is impossible to share the configuration since there is no way to update the changes applied.

The AWS CDK solves these issues in an elegant way: the ability to describe your infrastructure as a code using your favorite programming language.

Currently, at the time I'm writing this, it supports TypeScript, JavaScript, Python, Java, C#, and Go.

The source can be versioned in Git to keep the history of changes applied.

This tutorial will show how to create a Lambda Function with the AWS CDK v2 and deploy it in production.

Prerequisites

To achieve this, here are some elements required:

Install the CDK globally

We need to install the Node.js package to use the CDK commands through the CLI. It will be helpful to perform tasks such as:

  • Generate a new AWS CDK project that will define our infrastructure.
  • Generate the CloudFormation template from the TypeScript code.
  • Build and deploy the infrastructure AWS.

Run the command to globally install the AWS CDK version 2:


npm install -g cdk

Check if the installation succeeded by running: cdk --version

Create the project with AWS CDK v2

Now, create a folder that will hold the source code of our project.


mkdir lambda-node-cdk

cd lambda-node-cdk

Initialize a blank AWS CDK project by running the command below:


cdk init --language typescript

The command will generate many files and install the necessary Node.js dependencies required. Below is the project structure:

Project structure of Node.js app created by the AWS CDK.
Project structure of Node.js app created by the AWS CDK.

This is a classic minimal Node.js project with TypeScript, but there are some files to focus on:

  • lib/lambda-node-cdk-stack.ts is where your CDK application's main stack is defined. This is the file we'll be spending most of our time in.
  • bin/lambda-node-cdk.ts It is the entry point of the CDK application. It will load the stack defined in lib/lambda-node-cdk-stack.ts.
  • test/lambda-node-cdk.test.ts is where we write tests for our stack defined in lib/lambda-node-cdk-stack.
  • cdk.json file tells the toolkit on how to run your app. In our case, it will be npx ts-node bin/lambda-node-cdk.ts.

We will focus on the lib/lambda-node-cdk.ts file to create our Lambda Function.

Create the Lambda Function with AWS CDK v2

The AWS CDK v2 provides a single Node.js package named aws-cdk-lib to define the infrastructure of most AWS services in TypeScript.

Importing methods related to an AWS service follows this pattern:


import * as service from 'aws-cdk-lib/aws-<service>';

Here are some examples:

  • For AWS Lambda: import * as lambda from aws-cdk-lib/aws-lambda
  • For AWS SQS: import * as sqs from aws-cdk-lib/aws-sqs
  • For AWS S3: import * as s3 from aws-cdk-lib/aws-s3

Update the file lib/lambda-node-cdk.ts to create the Lambda Function with the required properties:


import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class LambdaNodeCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new lambda.Function(this, 'LambdaNodeStack', {
      code: lambda.Code.fromAsset('./src'),
      functionName: "lambdaNode",
      handler: 'index.handler',
      memorySize: 1024,
      runtime: lambda.Runtime.NODEJS_18_X,
      timeout: cdk.Duration.seconds(300),
    });
  }
}

Let's explain what we did here:

  • We create a Lambda Function called lambdaNode Running on Node.js 18 with 1GB of memory allocated.
  • The time-out is set to 300 seconds, which is equal to 5 minutes.
  • The property "code" indicates the folder's path to find the code to run when the Lambda is triggered. It is located in the "src" folder at the project root. It doesn't exist yet.
  • The property "handler" indicates the entry file and then the function name executed in this file. So the value index.handler can be broken down into:
    • index: the file called index.js inside the src directory.
    • handler: the function inside the index.js to execute.

Write the code to execute by the Lambda Function

In the infrastructure defined earlier, expect to find the code to execute in the "src" folder in a file named "index.js"; let's create them with the code below:


mkdir src

touch src/index.js

Open the file index.js and add the content below:


exports.handler = async function(event) {
  return {
    statusCode: 200,
    headers: { "Content-Type": "text/json" },
    body: JSON.stringify({ message: "Hello from my Lambda node!" })
  };
};

When we will trigger the Lambda function, it will respond with the message:


{ "message": "Hello from my Lambda node!" }

Deploy the Lambda Function on AWS

The first step to deploying Lambda Function is to generate the CloudFormation template from the AWS CDK code. It will also check if there is no error in the stack definition and throw an error if it is the case.

Run the command below to do that:


cdk synth

This command will output the CloudFormation stack to be created in the console and generate a folder named "cdk.out" containing the template and the assets files.

Bootstrap the deployment environment on AWS

This action creates a CloudFormation stack that includes resources used for the toolkit's operation, like an S3 bucket to store templates and assets during deployment.

It is required to run only if it is the first time you deploy with the AWS CDK on an AWS account, so you can skip this if you have already done it.

Run the command below to bootstrap the environment:


cdk bootstrap

⏳  Bootstrapping environment aws://123456789012/eu-west-1...

Once done, we can deploy our project with the command below:


cdk deploy

You will have the output below; press the touch y to continue...

The summary of resources to create on AWS.
The summary of resources to create on AWS.

Wait for the process to complete, then go to the AWS console to test your lambda function.

View the Lambda Functions in the AWS console.
View the Lambda Functions in the AWS console.

We see our lambda function; click on the Function name to go to the detail page.

Page with the details about the Lambda Function.
Page with the details about the Lambda Function.

Click on the "Test" button to trigger the Lambda Function. You will see the execution result on the page.

The execution of the Lambda was completed successfully.
The execution of the Lambda was completed successfully.

You have successfully deployed a Lambda Function with the AWS CDK v2.

Using Typescript and Esbuild to deploy a Lambda Function with AWS CDK
Do you want to know how to deploy your Lambda function written in Typescript using AWS CDK? We will use Docker and Esbuild to test it locally and deploy it in production.

Test the Lambda Function with the Function URL

In April 2022, AWS announced an interesting feature for Lambda Function called Function URL, a built-in HTTPS endpoint for a Lambda Function.

With this feature, when you deploy a Lambda Function on AWS, a URL is generated to trigger the Lambda Function through HTTP. This makes it easier to test your Function without going through multiple clicks in the AWS console.

The feature is disabled by default, so you must configure it; since we defined our Lambda Function with the AWS CDK, let's update it to configure the Function URL.

Update the code of the file lib/lambda-node-cdk.ts with the code below:


import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class LambdaNodeCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const lambdaFunction = new lambda.Function(this, 'LambdaNodeStack', {
      code: lambda.Code.fromAsset('./src'),
      functionName: "lambdaNode",
      handler: 'index.handler',
      memorySize: 1024,
      runtime: lambda.Runtime.NODEJS_18_X,
      timeout: cdk.Duration.seconds(300),
    });

    const lambdaFunctionUrl = lambdaFunction.addFunctionUrl({
      authType: lambda.FunctionUrlAuthType.NONE,
      invokeMode: lambda.InvokeMode.BUFFERED,
    });

    new cdk.CfnOutput(this, 'LambdaNodeUrl', {
      value: lambdaFunctionUrl.url,
    });
  }
}

In the instance of our Lambda Function definition, we call the method addFunctionUrl() with two options:

  • authType: is the authentication mode authorizing/blocking the caller to invoke the Lambda Function; we set it to "NONE" so anyone having the Function URL can invoke it.
  • invokeMode: is how our Lambda Function will return the response, which can be buffered or a stream.

Let's deploy the code on AWS by running the command below:


cdk synth

cdk deploy

We can see the new changes to apply to our Lambda Function in the Function URL. Once deployed, the URL is printed in the terminal:

Re-deploy the infrastructure changes on AWS.
Re-deploy the infrastructure changes on AWS.

Copy the Lambda Function URL, open the browser tab, and paste it. You get the following output in your browser:

Calling the Lambda Function URL in the browser.

Test the Lambda Function locally

We tested our Lambda Function in production, but in a classic software development lifecycle, we usually test the Lambda Function locally before deploying it in production.

To do it with the AWS CDK, we will use the AWS SAM CLI.

The AWS SAM CLI provides a command to invoke the Lambda Function locally. Follow the AWS documentation guide to install it on your computer.

You will need Docker running on your computer because the SAM will pull the Docker image of Node.js 18 for the Lambda Function in the ECR repository.

Pulling an image from the public ECR repository requires an authentication; run the command below to authenticate:


aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws

The syntax to invoke the Lambda Function defined with the AWS CDK is the following:


sam local invoke -t <CloudFormationTemplate> <FunctionStackName>

  • <CloudFormationTemplate>: is the path to the CloudFormation template of the stack, which is generated in the folder "cdk.out" after executing the command cdk synth.
  • <FunctionStackName>: is the name of your Lambda Function stack you set in the file lib/lambda-node-cdk-stack.ts.

Run the command below to invoke your Lambda Function with SAM CLI:


cdk synth

sam local invoke -t ./cdk.out/LambdaNodeCdkStack.template.json LambdaNodeStack

We get the output below:

Test the Lambda Function locally with the AWS SAM CLI.
Test the Lambda Function locally with the AWS SAM CLI.

You can now test your function locally before deploying it in production.

Wrap up

Deploying a Lambda Function with AWS CDK can be summarized in the following steps:

  • Generate the AWS CDK project.
  • Write the code of your infrastructure in TypeScript.
  • Synthesize the infrastructure code to generate the CloudFormation template.
  • Deploy the resources on AWS.

We deployed a Lambda Function returning a simple message when invoked, but you can go further by building a Backend API with the AWS CDK, as I show in this tutorial.

Find the final code source on the GitHub Repository.

Follow me on Twitter or subscribe to my newsletter to avoid missing the upcoming posts and the tips and tricks I occasionally share.