Guide to Creating Lambda Functions within AWS CDK Constructs
Tags: typescript, aws, awslambda, node
Published: 2024-02-12 14:26:53

In this comprehensive guide, we'll explore three distinct approaches to creating AWS Lambda functions within AWS Cloud Development Kit (CDK) constructs, leveraging TypeScript for a seamless development experience. AWS CDK simplifies the process of defining cloud infrastructure as code, providing a higher-level abstraction over AWS CloudFormation templates.

## Introduction to AWS CDK

The AWS Cloud Development Kit (CDK) is an open-source software development framework that enables developers to define cloud infrastructure using familiar programming languages like TypeScript. With AWS CDK, you can provision and manage AWS resources easily, allowing for efficient infrastructure as code (IaC) development.

## Introduction to AWS Lambda Functions

AWS Lambda is a serverless compute service that allows you to run code without managing servers. It automatically scales and manages compute resources, enabling you to focus solely on your code's functionality. Lambda functions can be triggered by various AWS services, responding to events such as S3 bucket changes, DynamoDB updates, or HTTP requests via Amazon API Gateway.

Now, let's delve into three different ways of creating Lambda functions within AWS CDK constructs using TypeScript.

## Inline Code

```typescript
import { Stack, StackProps } from "aws-cdk-lib";
import * as lambda from "aws-cdk-lib/aws-lambda";
import { Construct } from "constructs";

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

    const lambdaFunction = new lambda.Function(this, "LambdaHandler", {
      code: lambda.Code.fromInline(`
        exports.handler = async (event) => {
          console.log('request:', JSON.stringify(event, undefined, 2));

          return {
            statusCode: 200,
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ message: 'Hello world' }),
          };
        };
      `),
      handler: "index.handler",
      runtime: lambda.Runtime.NODEJS_18_X,
    });
  }
}
```

This method utilizes inline code, leveraging the `Function` construct from the `@aws-cdk/aws-lambda` package. The runtime code is embedded as a string using the `lambda.Code.fromInline` method. Keep in mind that this approach is limited to supported runtimes.

## Specify a Separate File

Create a file called `index.ts` in the `lambda` directory with the following code:

```typescript
// File: lambda/index.ts

exports.handler = async (event) => {
  console.log("request:", JSON.stringify(event, undefined, 2));

  return {
    statusCode: 200,
    body: JSON.stringify({ message: "Hello world" }),
  };
};
```

Now, modify the AWS CDK Stack to use this external file:

```typescript
import { Stack, StackProps } from "aws-cdk-lib";
import * as lambda from "aws-cdk-lib/aws-lambda";
import { Construct } from "constructs";

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

    const lambdaFunction = new lambda.Function(this, "LambdaHandler", {
      runtime: lambda.Runtime.NODEJS_18_X,
      code: lambda.Code.fromAsset("lambda"),
      handler: "index.handler",
    });
  }
}
```

In this approach, the runtime code is moved to a separate file (`index.ts`) in the local filesystem, and the path for the directory of the file is given to the `fromAsset` method of the `lambda.Code` class. The `handler` property now has the value of the filename and function name (`index.handler`).

## NodejsFunction

```typescript
import { Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodeJS from "aws-cdk-lib/aws-lambda-nodejs";

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

    const lambdaFunction = new nodeJS.NodejsFunction(this, "LambdaHandler", {
      entry: "/path/to/my/file.ts",
      handler: "handler",
      runtime: lambda.Runtime.NODEJS_18_X,
    });
  }
}
```

In this approach, the `NodejsFunction` construct from `aws-cdk-lib/aws-lambda-nodejs` is used. This construct creates a Lambda function with automatic transpiling and bundling of TypeScript or JavaScript code, resulting in smaller Lambda packages that contain only the code and dependencies needed to run the function. It uses `esbuild` under the hood.

The `entry` property specifies the entry point for the TypeScript file containing your Lambda function code. Make sure to replace '/path/to/my/file.ts' with the actual path to your TypeScript file. The `handler` property sets the entry point for the Lambda function, and the `runtime` property specifies the Node.js version as 18.x.

## Conclusion

- Inline code is a quick and easy way to write a Lambda function but has a limited runtime and lacks IDE support during code writing.
- Separating the file keeps the CDK stack code and Lambda function code separated and supports IDE. However, external dependencies, except the AWS SDK, are not supported.
- The third approach, using NodejsFunction, results in smaller Lambda packages but requires another build step and increases the size of the CDK construct.