Working with layers for TypeScript Lambda functions - AWS Lambda

Working with layers for TypeScript Lambda functions

A Lambda layer is a .zip file archive that contains supplementary code or data. Layers usually contain library dependencies, a custom runtime, or configuration files. Creating a layer involves three general steps:

  1. Package your layer content. This means creating a .zip file archive that contains the dependencies you want to use in your functions.

  2. Create the layer in Lambda.

  3. Add the layer to your functions.

This topic contains steps and guidance on how to properly package and create a Node.js Lambda layer with external library dependencies. Additionally, this topic explains how use your layer with a function written in TypeScript.

Prerequisites

To follow the steps in this section, you must have the following:

Throughout this topic, we reference the layer-nodejs sample application in the aws-lambda-developer-guide GitHub repository. This application contains scripts that will package the lodash library into a Lambda layer. The layer directory contains the scripts to generate the layer. The application also contains a TypeScript sample function in the function-ts directory that uses the dependency from the layer. After creating a layer, you can transpile, deploy and invoke the corresponding function to verify that everything works. This document walks through how to create, package, deploy and test this layer using the TypeScript sample function.

This sample application uses the Node.js 20 runtime. If you add additional dependencies to your layer, they must be compatible with Node.js 20.

Node.js layer compatibility with the Lambda runtime environment

When you package code in a Node.js layer, you specify the Lambda runtime environments that the code is compatible with. To assess code compatibility with a runtime, consider what versions of Node.js, what operating systems, and what instruction set architectures the code is designed for.

Lambda Node.js runtimes specify their Node.js version and operating system. In this document, you will use the Node.js 20 runtime, which is based on AL2023. For more information about runtime versions, see Supported runtimes. When you create a Lambda function, you specify the instruction set architecture. In this document, you will use the arm64 architecture. For more information about architectures in Lambda, see Selecting and configuring an instruction set architecture for your Lambda function.

When you use code provided in a package, each package maintainer independently defines their compatibility. Most Node.js development is designed to work independently of operating system and instruction set architecture. Additionally, breaking incompatibilities with new Node.js versions are not that common. Expect to spend more of your time assessing compatibility between packages than assessing package compatibility with Node.js version, operating system, or instruction set architecture.

Sometimes Node.js packages include compiled code, which require you to consider operating system and instruction set architecture compatibility. If you do need to assess code compatibility for your packages, you will need to inspect the packages and their documentation. Packages in NPM can specify their compatibility through the engines, os, and cpu fields of their package.json manifest file. For more information about package.json files, see package.json in the NPM documentation.

Layer paths for Node.js runtimes

When you add a layer to a function, Lambda loads the layer content into the execution environment. If your layer packages dependencies in specific folder paths, the Node.js execution environment will recognize the modules, and you can reference the modules from your function code.

To ensure that your modules are picked up, package them into your layer .zip file in one of the following folder paths. These files are stored in /opt, and the folder paths are loaded into the PATH environment variable.

  • nodejs/node_modules

  • nodejs/nodeX/node_modules

For example, the resulting layer .zip file that you create in this tutorial has the following directory structure:

layer_content.zip └ nodejs └ node20 └ node_modules └ lodash └ <other potential dependencies> └ ...

You will put the lodash library in the nodejs/node20/node_modules directory. This ensures that Lambda can locate the library during function invocations.

Packaging the layer content

In this example, you package the lodash library in a layer .zip file. Complete the following steps to install and package the layer content.

To install and package your layer content
  1. Clone the aws-lambda-developer-guide repository from GitHub, which contains the sample code that you need in the sample-apps/layer-nodejs directory.

    git clone https://github.com/awsdocs/aws-lambda-developer-guide.git
  2. Navigate to the layer directory of the layer-nodejs sample app. This directory contains the scripts that you use to create and package the layer properly.

    cd aws-lambda-developer-guide/sample-apps/layer-nodejs/layer
  3. Ensure the package.json file lists lodash. This file defines the dependencies that you want to include in the layer. You can update this file to include any dependencies that you want in your layer.

    Note

    The package.json used in this step is not stored or used with your dependencies after they are uploaded to a Lambda layer. It is only used in the layer packaging process, and does not specify a run command and compatibility as the file would in a Node.js application or published package.

  4. Ensure that you have shell permission to run the scripts in the layer directory.

    chmod 744 1-install.sh && chmod 744 2-package.sh
  5. Run the 1-install.sh script using the following command:

    ./1-install.sh

    This script runs npm install, which reads your package.json and downloads the dependencies defined inside of it.

    Example 1-install.sh
    npm install .
  6. Run the 2-package.sh script using the following command:

    ./2-package.sh

    This script copies the contents from the node_modules directory into a new directory named nodejs/node20. It then zips the contents of the nodejs directory into a file named layer_content.zip. This is the .zip file for your layer. You can unzip the file and verify that it contains the correct file structure, as shown in the Layer paths for Node.js runtimes section.

    Example 2-package.sh
    mkdir -p nodejs/node20 cp -r node_modules nodejs/node20/ zip -r layer_content.zip nodejs

Creating the layer

Take the layer_content.zip file that you generated in the previous section and upload it as a Lambda layer. You can upload a layer using the AWS Management Console or the Lambda API via the AWS Command Line Interface (AWS CLI). When you upload your layer .zip file, in the following PublishLayerVersion AWS CLI command, specify nodejs20.x as the compatible runtime and arm64 as the compatible architecture.

aws lambda publish-layer-version --layer-name nodejs-lodash-layer \ --zip-file fileb://layer_content.zip \ --compatible-runtimes nodejs20.x \ --compatible-architectures "arm64"

From the response, note the LayerVersionArn, which looks like arn:aws:lambda:us-east-1:123456789012:layer:nodejs-lodash-layer:1. You'll need this Amazon Resource Name (ARN) in the next step of this tutorial, when you add the layer to your function.

Adding the layer to your function

Deploy a sample Lambda function that uses the lodash library in its function code, then attach the layer you created. To create a Lambda function using function code written in TypeScript, you must transpile the TypeScript to JavaScript for use by the Node.js runtime. For more information about this process, see Define Lambda function handler in TypeScript. For better compatibility, use tsc to transpile your TypeScript module when you distribute your dependencies with layers. If you bundle your dependencies, consider using esbuild. For more information about bundling with esbuild, see Deploy transpiled TypeScript code in Lambda with .zip file archives.

To deploy the function, you need an execution role. For more information, see Defining Lambda function permissions with an execution role. If you don't have an existing execution role, follow the steps in the collapsible section. Otherwise, skip to the next section to deploy the function.

To create an execution role
  1. Open the roles page in the IAM console.

  2. Choose Create role.

  3. Create a role with the following properties.

    • Trusted entityLambda.

    • PermissionsAWSLambdaBasicExecutionRole.

    • Role namelambda-role.

    The AWSLambdaBasicExecutionRole policy has the permissions that the function needs to write logs to CloudWatch Logs.

The sample function code uses the lodash _.findLastIndex method to read through an array of objects. It compares the objects against a criteria to find the index of a match. Then, it returns the index and value of the object in the Lambda response.

import { Handler } from 'aws-lambda'; import * as _ from 'lodash'; type User = { user: string; active: boolean; } type UserResult = { statusCode: number; body: string; } const users: User[] = [ { 'user': 'Carlos', 'active': true }, { 'user': 'Gil-dong', 'active': false }, { 'user': 'Pat', 'active': false } ]; export const handler: Handler<any, UserResult> = async (): Promise<UserResult> => { let out = _.findLastIndex(users, (user: User) => { return user.user == 'Pat'; }); const response = { statusCode: 200, body: JSON.stringify(out + ", " + users[out].user), }; return response; };
To deploy the Lambda function
  1. Navigate to the function-ts/ directory of the layer-nodejs sample application. If you're currently in the layer/ directory of the layer-nodejs sample application, then run the following command:

    cd ../function-ts
  2. Install the development dependencies listed in the package.json using the following command:

    npm install
  3. Run the build task defined in the package.json to transpile and package your function code into a .zip file. Use the following command:

    npm run build
  4. Deploy the function. In the following AWS CLI command, replace the --role parameter with your execution role ARN:

    aws lambda create-function --function-name nodejs_function_with_layer \ --runtime nodejs20.x \ --architectures "arm64" \ --handler index.handler \ --role arn:aws:iam::123456789012:role/lambda-role \ --zip-file fileb://dist/index.zip
  5. Attach the layer to your function. In the following AWS CLI command, replace the --layers parameter with the layer version ARN that you noted earlier:

    aws lambda update-function-configuration --function-name nodejs_function_with_layer \ --cli-binary-format raw-in-base64-out \ --layers "arn:aws:lambda:us-east-1:123456789012:layer:nodejs-lodash-layer:1"
  6. Invoke your function to verify it works using the following AWS CLI command:

    aws lambda invoke --function-name nodejs_function_with_layer \ --cli-binary-format raw-in-base64-out \ --payload '{}' response.json

    You should see output that looks like this:

    { "StatusCode": 200, "ExecutedVersion": "$LATEST" }

    The output response.json file contains details about the response.

You can now delete the resources that you created for this tutorial, unless you want to retain them. By deleting AWS resources that you're no longer using, you prevent unnecessary charges to your AWS account.

To delete the Lambda layer
  1. Open the Layers page of the Lambda console.

  2. Select the layer that you created.

  3. Choose Delete, then choose Delete again.

To delete the Lambda function
  1. Open the Functions page of the Lambda console.

  2. Select the function that you created.

  3. Choose Actions, Delete.

  4. Type delete in the text input field and choose Delete.