

# Building Lambda functions with Node.js
<a name="lambda-nodejs"></a>

You can run JavaScript code with Node.js in AWS Lambda. Lambda provides [runtimes](lambda-runtimes.md) for Node.js that run your code to process events. Your code runs in an environment that includes the AWS SDK for JavaScript, with credentials from an AWS Identity and Access Management (IAM) role that you manage. To learn more about the SDK versions included with the Node.js runtimes, see [Runtime-included SDK versions](#nodejs-sdk-included).

Lambda supports the following Node.js runtimes.<a name="nodejs-supported-runtimes"></a>


| Name | Identifier | Operating system | Deprecation date | Block function create | Block function update | 
| --- | --- | --- | --- | --- | --- | 
|  Node.js 24  |  `nodejs24.x`  |  Amazon Linux 2023  |   Apr 30, 2028   |   Jun 1, 2028   |   Jul 1, 2028   | 
|  Node.js 22  |  `nodejs22.x`  |  Amazon Linux 2023  |   Apr 30, 2027   |   Jun 1, 2027   |   Jul 1, 2027   | 
|  Node.js 20  |  `nodejs20.x`  |  Amazon Linux 2023  |   Apr 30, 2026   |   Aug 31, 2026   |   Sep 30, 2026   | 

**To create a Node.js function**

1. Open the [Lambda console](https://console.aws.amazon.com/lambda).

1. Choose **Create function**.

1. Configure the following settings:
   + **Function name**: Enter a name for the function.
   + **Runtime**: Choose **Node.js 24.x**.

1. Choose **Create function**.

The console creates a Lambda function with a single source file named `index.mjs`. You can edit this file and add more files in the built-in code editor. In the **DEPLOY** section, choose **Deploy** to update your function's code. Then, to run your code, choose **Create test event** in the **TEST EVENTS** section.

The `index.mjs` file exports a function named `handler` that takes an event object and a context object. This is the [handler function](nodejs-handler.md) that Lambda calls when the function is invoked. The Node.js function runtime gets invocation events from Lambda and passes them to the handler. In the function configuration, the handler value is `index.handler`.

When you save your function code, the Lambda console creates a .zip file archive deployment package. When you develop your function code outside of the console (using an IDE) you need to [create a deployment package](nodejs-package.md) to upload your code to the Lambda function.

The function runtime passes a context object to the handler, in addition to the invocation event. The [context object](nodejs-context.md) contains additional information about the invocation, the function, and the execution environment. More information is available from environment variables.

Your Lambda function comes with a CloudWatch Logs log group. The function runtime sends details about each invocation to CloudWatch Logs. It relays any [logs that your function outputs](nodejs-logging.md) during invocation. If your function returns an error, Lambda formats the error and returns it to the invoker.

**Topics**
+ [Runtime-included SDK versions](#nodejs-sdk-included)
+ [Using keep-alive for TCP connections](#nodejs-keep-alive)
+ [CA certificate loading](#nodejs-certificate-loading)
+ [Experimental Node.js features](#nodejs-experimental-features)
+ [Define Lambda function handler in Node.js](nodejs-handler.md)
+ [Deploy Node.js Lambda functions with .zip file archives](nodejs-package.md)
+ [Deploy Node.js Lambda functions with container images](nodejs-image.md)
+ [Working with layers for Node.js Lambda functions](nodejs-layers.md)
+ [Using the Lambda context object to retrieve Node.js function information](nodejs-context.md)
+ [Log and monitor Node.js Lambda functions](nodejs-logging.md)
+ [Instrumenting Node.js code in AWS Lambda](nodejs-tracing.md)

## Runtime-included SDK versions
<a name="nodejs-sdk-included"></a>

All [supported Lambda Node.js runtimes](#nodejs-supported-runtimes) include a specific minor version of the AWS SDK for JavaScript v3, not the [latest version](https://github.com/aws/aws-sdk-js-v3/releases). The specific minor version that's included in the runtime depends on the runtime version and your AWS Region. To find the specific version of the SDK included in the runtime that you're using, create a Lambda function with the following code.

**Example index.mjs**  

```
import packageJson from '@aws-sdk/client-s3/package.json' with { type: 'json' };

export const handler = async () => ({ version: packageJson.version });
```
This returns a response in the following format:  

```
{
  "version": "3.632.0"
}
```

For more information, see [Using the SDK for JavaScript v3 in your handler](nodejs-handler.md#nodejs-example-sdk-usage).

## Using keep-alive for TCP connections
<a name="nodejs-keep-alive"></a>

The default Node.js HTTP/HTTPS agent creates a new TCP connection for every new request. To avoid the cost of establishing new connections, keep-alive is enabled by default in all [supported Node.js runtimes](#nodejs-supported-runtimes). Keep-alive can reduce request times for Lambda functions that make multiple API calls using the SDK.

To disable keep-alive, see [Reusing connections with keep-alive in Node.js](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html) in the *AWS SDK for JavaScript 3.x Developer Guide*. For more information about using keep-alive, see [HTTP keep-alive is on by default in modular AWS SDK for JavaScript](https://aws.amazon.com/blogs/developer/http-keep-alive-is-on-by-default-in-modular-aws-sdk-for-javascript/) on the AWS Developer Tools Blog.

## CA certificate loading
<a name="nodejs-certificate-loading"></a>

For Node.js runtime versions up to Node.js 18, Lambda automatically loads Amazon-specific CA (certificate authority) certificates to make it easier for you to create functions that interact with other AWS services. For example, Lambda includes the Amazon RDS certificates necessary for validating the [server identity certificate](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html) installed on your Amazon RDS database. This behavior can have a performance impact during cold starts.

Starting with Node.js 20, Lambda no longer loads additional CA certificates by default. The Node.js 20 runtime contains a certificate file with all Amazon CA certificates located at `/var/runtime/ca-cert.pem`. To restore the same behavior from Node.js 18 and earlier runtimes, set the `NODE_EXTRA_CA_CERTS` [environment variable](configuration-envvars.md) to `/var/runtime/ca-cert.pem`.

For optimal performance, we recommend bundling only the certificates that you need with your deployment package and loading them via the `NODE_EXTRA_CA_CERTS` environment variable. The certificates file should consist of one or more trusted root or intermediate CA certificates in PEM format. For example, for RDS, include the required certificates alongside your code as `certificates/rds.pem`. Then, load the certificates by setting `NODE_EXTRA_CA_CERTS` to `/var/task/certificates/rds.pem`.

## Experimental Node.js features
<a name="nodejs-experimental-features"></a>

The upstream Node.js language releases enable some experimental features by default. Lambda disables these features to ensure runtime stability and consistent performance. The following table lists the experimental features that Lambda disables.


| Experimental feature | Supported Node.js versions | Node.js flag applied by Lambda | Lambda flag to re-enable | 
| --- | --- | --- | --- | 
|  Support for importing modules using require in ES modules  |  Node.js 20, Node.js 22  |  `--no-experimental-require-module`  |  `--experimental-require-module`  | 
|  Support for automatically detecting ES vs CommonJS modules  |  Node.js 22  |  `--no-experimental-detect-module`  |  `--experimental-detect-module`  | 

To enable a disabled experimental feature, set the re-enable flag in the `NODE_OPTIONS` environment variable. For example, to enable ES module require support, set `NODE_OPTIONS` to `--experimental-require-module`. Lambda detects this override and removes the corresponding disable flag.

**Important**  
 Using experimental features can lead to instability and performance issues. These features might be changed or removed in future Node.js versions. Functions that use experimental features aren't eligible for the Lambda Service Level Agreement (SLA) or AWS Support.

# Define Lambda function handler in Node.js
<a name="nodejs-handler"></a>

The Lambda function *handler* is the method in your function code that processes events. When your function is invoked, Lambda runs the handler method. Your function runs until the handler returns a response, exits, or times out.

This page describes how to work with Lambda function handlers in Node.js, including options for project setup, naming conventions, and best practices. This page also includes an example of a Node.js Lambda function that takes in information about an order, produces a text file receipt, and puts this file in an Amazon Simple Storage Service (Amazon S3) bucket. For information about how to deploy your function after writing it, see [Deploy Node.js Lambda functions with .zip file archives](nodejs-package.md) or [Deploy Node.js Lambda functions with container images](nodejs-image.md).

**Topics**
+ [Setting up your Node.js handler project](#nodejs-handler-setup)
+ [Example Node.js Lambda function code](#nodejs-example-code)
+ [CommonJS and ES Modules](#nodejs-commonjs-es-modules)
+ [Node.js initialization](#nodejs-initialization)
+ [Handler naming conventions](#nodejs-handler-naming)
+ [Defining and accessing the input event object](#nodejs-example-input)
+ [Valid handler patterns for Node.js functions](#nodejs-handler-signatures)
+ [Using the SDK for JavaScript v3 in your handler](#nodejs-example-sdk-usage)
+ [Accessing environment variables](#nodejs-example-envvars)
+ [Using global state](#nodejs-handler-state)
+ [Code best practices for Node.js Lambda functions](#nodejs-best-practices)

## Setting up your Node.js handler project
<a name="nodejs-handler-setup"></a>

There are multiple ways to initialize a Node.js Lambda project. For example, you can create a standard Node.js project using `npm`, create an [AWS SAM application](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-init.html#using-sam-cli-init-new), or create an [AWS CDK application](lambda-cdk-tutorial.md#lambda-cdk-step-1).

To create the project using `npm`:

```
npm init
```

This command initializes your project and generates a `package.json` file that manages your project's metadata and dependencies.

Your function code lives in a `.js` or `.mjs` JavaScript file. In the following example, we name this file `index.mjs` because it uses an ES module handler. Lambda supports both ES module and CommonJS handlers. For more information, see [CommonJS and ES Modules](#nodejs-commonjs-es-modules).

A typical Node.js Lambda function project follows this general structure:

```
/project-root
  ├── index.mjs — Contains main handler
  ├── package.json — Project metadata and dependencies
  ├── package-lock.json — Dependency lock file
  └── node_modules/ — Installed dependencies
```

## Example Node.js Lambda function code
<a name="nodejs-example-code"></a>

The following example Lambda function code takes in information about an order, produces a text file receipt, and puts this file in an Amazon S3 bucket.

**Example index.mjs Lambda function**  

```
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

// Initialize the S3 client outside the handler for reuse
const s3Client = new S3Client();

/**
 * Lambda handler for processing orders and storing receipts in S3.
 * @param {Object} event - Input event containing order details
 * @param {string} event.order_id - The unique identifier for the order
 * @param {number} event.amount - The order amount
 * @param {string} event.item - The item purchased
 * @returns {Promise<string>} Success message
 */
export const handler = async(event) => {
    try {
        // Access environment variables
        const bucketName = process.env.RECEIPT_BUCKET;
        if (!bucketName) {
            throw new Error('RECEIPT_BUCKET environment variable is not set');
        }

        // Create the receipt content and key destination
        const receiptContent = `OrderID: ${event.order_id}\nAmount: $${event.amount.toFixed(2)}\nItem: ${event.item}`;
        const key = `receipts/${event.order_id}.txt`;

        // Upload the receipt to S3
        await uploadReceiptToS3(bucketName, key, receiptContent);

        console.log(`Successfully processed order ${event.order_id} and stored receipt in S3 bucket ${bucketName}`);
        return 'Success';
    } catch (error) {
        console.error(`Failed to process order: ${error.message}`);
        throw error;
    }
};

/**
 * Helper function to upload receipt to S3
 * @param {string} bucketName - The S3 bucket name
 * @param {string} key - The S3 object key
 * @param {string} receiptContent - The content to upload
 * @returns {Promise<void>}
 */
async function uploadReceiptToS3(bucketName, key, receiptContent) {
    try {
        const command = new PutObjectCommand({
            Bucket: bucketName,
            Key: key,
            Body: receiptContent
        });

        await s3Client.send(command);
    } catch (error) {
        throw new Error(`Failed to upload receipt to S3: ${error.message}`);
    }
}
```

This `index.mjs` file contains the following sections of code:
+ `import` block: Use this block to include libraries that your Lambda function requires, such as [AWS SDK clients](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/the-request-object.html).
+ `const s3Client` declaration: This initializes an [Amazon S3 client](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/) outside of the handler function. This causes Lambda to run this code during the [initialization phase](lambda-runtime-environment.md#runtimes-lifecycle-ib), and the client is preserved for [reuse across multiple invocations](lambda-runtime-environment.md#execution-environment-reuse).
+ JSDoc comment block: Define the input and output types for your handler using [JSDoc annotations](https://jsdoc.app/about-getting-started).
+ `export const handler`: This is the main handler function that Lambda invokes. When deploying your function, specify `index.handler` for the [Handler](https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html#lambda-CreateFunction-request-Handler) property. The value of the `Handler` property is the file name and the name of the exported handler method, separated by a dot.
+ `uploadReceiptToS3` function: This is a helper function that's referenced by the main handler function.

For this function to work properly, its [ execution role](lambda-intro-execution-role.md) must allow the `s3:PutObject` action. Also, ensure that you define the `RECEIPT_BUCKET` environment variable. After a successful invocation, the Amazon S3 bucket should contain a receipt file.

## CommonJS and ES Modules
<a name="nodejs-commonjs-es-modules"></a>

Node.js supports two module systems: CommonJS and ECMAScript modules (ES modules). Lambda recommends using ES modules as it supports top-level await, which enables asynchronous tasks to be completed during [execution environment initialization](#nodejs-initialization).

Node.js treats files with a `.cjs` file name extension as CommonJS modules while a `.mjs` extension denotes ES modules. By default, Node.js treats files with the `.js` file name extension as CommonJS modules. You can configure Node.js to treat `.js` files as ES modules by specifying the `type` as `module` in the function's `package.json` file. You can configure Node.js in Lambda to detect automatically whether a `.js` file should be treated as CommonJS or as an ES module by adding the `—experimental-detect-module` flag to the `NODE_OPTIONS` environment variable. For more information, see [Experimental Node.js features](lambda-nodejs.md#nodejs-experimental-features).

The following examples show function handlers written using both ES modules and CommonJS modules. The remaining examples on this page all use ES modules.

------
#### [ ES module example ]

**Example – ES module handler**  

```
const url = "https://aws.amazon.com/";

export const handler = async(event) => {
    try {
        const res = await fetch(url);
        console.info("status", res.status);
        return res.status;
    }
    catch (e) {
        console.error(e);
        return 500;
    }
};
```

------
#### [ CommonJS module example ]

**Example – CommonJS module handler**  

```
const https = require("https");
let url = "https://aws.amazon.com/";

exports.handler = async function (event) {
  let statusCode;
  await new Promise(function (resolve, reject) {
    https.get(url, (res) => {
        statusCode = res.statusCode;
        resolve(statusCode);
      }).on("error", (e) => {
        reject(Error(e));
      });
  });
  console.log(statusCode);
  return statusCode;
};
```

------

## Node.js initialization
<a name="nodejs-initialization"></a>

Node.js uses a non-blocking I/O model that supports efficient asynchronous operations using an event loop. For example, if Node.js makes a network call, the function continues to process other operations without blocking on a network response. When the network response is received, it is placed into the callback queue. Tasks from the queue are processed when the current task completes.

Lambda recommends using top-level await so that asynchronous tasks initiated during execution environment initialization are completed during initialization. Asynchronous tasks that are not completed during initialization will typically run during the first function invoke. This can cause unexpected behavior or errors. For example, your function initialization may make a network call to fetch a parameter from AWS Parameter Store. If this task is not completed during initialization, the value may be null during an invocation. There can also be a delay between initialization and invoke which can trigger errors in time-sensitive operations. In particular, AWS service calls can rely on time-sensitive request signatures, resulting in service call failures if the call is not completed during the initialization phase. Completing tasks during initialization typically improves cold-start performance, and first invoke performance when using Provisioned Concurrency. For more information, see our blog post [Using Node.js ES modules and top-level await in AWS Lambda](https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda).

## Handler naming conventions
<a name="nodejs-handler-naming"></a>

When you configure a function, the value of the [Handler](https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html#lambda-CreateFunction-request-Handler) setting is the file name and the name of the exported handler method, separated by a dot. The default for functions created in the console and for examples in this guide is `index.handler`. This indicates the `handler` method that's exported from the `index.js` or `index.mjs` file.

If you create a function in the console using a different file name or function handler name, you must edit the default handler name.

**To change the function handler name (console)**

1. Open the [Functions](https://console.aws.amazon.com/lambda/home#/functions) page of the Lambda console and choose your function.

1. Choose the **Code** tab.

1. Scroll down to the **Runtime settings** pane and choose **Edit**.

1. In **Handler**, enter the new name for your function handler.

1. Choose **Save**.

## Defining and accessing the input event object
<a name="nodejs-example-input"></a>

JSON is the most common and standard input format for Lambda functions. In this example, the function expects an input similar to the following:

```
{
    "order_id": "12345",
    "amount": 199.99,
    "item": "Wireless Headphones"
}
```

When working with Lambda functions in Node.js, you can define the expected shape of the input event using JSDoc annotations. In this example, we define the input structure in the handler's JSDoc comment:

```
/**
 * Lambda handler for processing orders and storing receipts in S3.
 * @param {Object} event - Input event containing order details
 * @param {string} event.order_id - The unique identifier for the order
 * @param {number} event.amount - The order amount
 * @param {string} event.item - The item purchased
 * @returns {Promise<string>} Success message
 */
```

After you define these types in your JSDoc comment, you can access the fields of the event object directly in your code. For example, `event.order_id` retrieves the value of `order_id` from the original input.

## Valid handler patterns for Node.js functions
<a name="nodejs-handler-signatures"></a>

We recommend that you use [async/await](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/using-async-await.html) to declare the function handler instead of using [callbacks](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/using-a-callback-function.html). Async/await is a concise and readable way to write asynchronous code, without the need for nested callbacks or chaining promises. With async/await, you can write code that reads like synchronous code, while still being asynchronous and non-blocking.

### async function handlers (recommended)
<a name="nodejs-handler-async"></a>

The `async` keyword marks a function as asynchronous, and the `await` keyword pauses the execution of the function until a `Promise` is resolved. The handler accepts the following arguments:
+ `event`: Contains the input data passed to your function.
+ `context`: Contains information about the invocation, function, and execution environment. For more information, see [Using the Lambda context object to retrieve Node.js function information](nodejs-context.md).

Here are the valid signatures for the async/await pattern:

```
export const handler = async (event) => { };
```

```
export const handler = async (event, context) => { };
```

### Synchronous function handlers
<a name="nodejs-handler-synchronous"></a>

Where your function does not perform any asynchronous tasks, you can use a synchronous function handler, using one of the following function signatures:

```
export const handler = (event) => { };
```

```
export const handler = (event, context) => { };
```

### Response streaming function handlers
<a name="nodejs-handler-response-streaming"></a>

Lambda supports response streaming with Node.js. Response streaming function handlers use the `awslambda.streamifyResponse()` decorator and take 3 parameters: `event`, `responseStream`, and `context`. The function signature is:

```
export const handler = awslambda.streamifyResponse(async (event, responseStream, context) => { });
```

For more information, see [Response streaming for Lambda functions](configuration-response-streaming.md).

### Callback-based function handlers
<a name="nodejs-handler-callback"></a>

**Note**  
Callback-based function handlers are only supported up to Node.js 22. Starting from Node.js 24, asynchronous tasks should be implemented using async function handlers.

Callback-based function handlers must use the event, context, and callback arguments. Example:

```
export const handler = (event, context, callback) => { };
```

The callback function expects an `Error` and a response, which must be JSON-serializable. The function continues to execute until the [event loop](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/) is empty or the function times out. The response isn't sent to the invoker until all event loop tasks are finished. If the function times out, an error is returned instead. You can configure the runtime to send the response immediately by setting [context.callbackWaitsForEmptyEventLoop](nodejs-context.md) to false.

**Example – HTTP request with callback**  
The following example function checks a URL and returns the status code to the invoker.  

```
import https from "https";
let url = "https://aws.amazon.com/";

export const handler = (event, context, callback) => {
  https.get(url, (res) => {
    callback(null, res.statusCode);
  }).on("error", (e) => {
    callback(Error(e));
  });
};
```

## Using the SDK for JavaScript v3 in your handler
<a name="nodejs-example-sdk-usage"></a>

Often, you’ll use Lambda functions to interact with or make updates to other AWS resources. The simplest way to interface with these resources is to use the AWS SDK for JavaScript. All [supported Lambda Node.js runtimes](lambda-nodejs.md#nodejs-supported-runtimes) include the [SDK for JavaScript version 3](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/). However, we strongly recommend that you include the AWS SDK clients that you need in your deployment package. This maximizes [backward compatibility](runtimes-update.md#runtime-update-compatibility) during future Lambda runtime updates. Only rely on the runtime-provided SDK when you can't include additional packages (for example, when using the Lambda console code editor or inline code in an AWS CloudFormation template).

To add SDK dependencies to your function, use the `npm install` command for the specific SDK clients that you need. In the example code, we used the [Amazon S3 client](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/). Add this dependency by running the following command in the directory that contains your `package.json` file:

```
npm install @aws-sdk/client-s3
```

In the function code, import the client and commands that you need, as the example function demonstrates:

```
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
```

Then, initialize an [Amazon S3 client](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/):

```
const s3Client = new S3Client();
```

In this example, we initialized our Amazon S3 client outside of the main handler function to avoid having to initialize it every time we invoke our function. After you initialize your SDK client, you can then use it to make API calls for that AWS service. The example code calls the Amazon S3 [PutObject](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/command/PutObjectCommand/) API action as follows:

```
const command = new PutObjectCommand({
    Bucket: bucketName,
    Key: key,
    Body: receiptContent
});
```

## Accessing environment variables
<a name="nodejs-example-envvars"></a>

In your handler code, you can reference any [environment variables](configuration-envvars.md) by using `process.env`. In this example, we reference the defined `RECEIPT_BUCKET` environment variable using the following lines of code:

```
// Access environment variables
const bucketName = process.env.RECEIPT_BUCKET;
if (!bucketName) {
    throw new Error('RECEIPT_BUCKET environment variable is not set');
}
```

## Using global state
<a name="nodejs-handler-state"></a>

Lambda runs your static code during the [initialization phase](lambda-runtime-environment.md#runtimes-lifecycle-ib) before invoking your function for the first time. Resources created during initialization stay in memory between invocations, so you can avoid having to create them every time you invoke your function.

In the example code, the S3 client initialization code is outside the handler. The runtime initializes the client before the function handles its first event, and the client remains available for reuse across all invocations.

## Code best practices for Node.js Lambda functions
<a name="nodejs-best-practices"></a>

Follow these guidelines when building Lambda functions:
+ **Separate the Lambda handler from your core logic.** This allows you to make a more unit-testable function.
+ **Control the dependencies in your function's deployment package. ** The AWS Lambda execution environment contains a number of libraries. For the Node.js and Python runtimes, these include the AWS SDKs. To enable the latest set of features and security updates, Lambda will periodically update these libraries. These updates may introduce subtle changes to the behavior of your Lambda function. To have full control of the dependencies your function uses, package all of your dependencies with your deployment package. 
+ **Minimize the complexity of your dependencies.** Prefer simpler frameworks that load quickly on [execution environment](lambda-runtime-environment.md) startup.
+ **Minimize your deployment package size to its runtime necessities. ** This will reduce the amount of time that it takes for your deployment package to be downloaded and unpacked ahead of invocation.

**Take advantage of execution environment reuse to improve the performance of your function.** Initialize SDK clients and database connections outside of the function handler, and cache static assets locally in the `/tmp` directory. Subsequent invocations processed by the same instance of your function can reuse these resources. This saves cost by reducing function run time.

To avoid potential data leaks across invocations, don’t use the execution environment to store user data, events, or other information with security implications. If your function relies on a mutable state that can’t be stored in memory within the handler, consider creating a separate function or separate versions of a function for each user.

**Use a keep-alive directive to maintain persistent connections.** Lambda purges idle connections over time. Attempting to reuse an idle connection when invoking a function will result in a connection error. To maintain your persistent connection, use the keep-alive directive associated with your runtime. For an example, see [Reusing Connections with Keep-Alive in Node.js](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html).

**Use [environment variables](configuration-envvars.md) to pass operational parameters to your function.** For example, if you are writing to an Amazon S3 bucket, instead of hard-coding the bucket name you are writing to, configure the bucket name as an environment variable.

**Avoid using recursive invocations** in your Lambda function, where the function invokes itself or initiates a process that may invoke the function again. This could lead to unintended volume of function invocations and escalated costs. If you see an unintended volume of invocations, set the function reserved concurrency to `0` immediately to throttle all invocations to the function, while you update the code.

**Do not use non-documented, non-public APIs** in your Lambda function code. For AWS Lambda managed runtimes, Lambda periodically applies security and functional updates to Lambda's internal APIs. These internal API updates may be backwards-incompatible, leading to unintended consequences such as invocation failures if your function has a dependency on these non-public APIs. See [the API reference](https://docs.aws.amazon.com/lambda/latest/api/welcome.html) for a list of publicly available APIs.

**Write idempotent code.** Writing idempotent code for your functions ensures that duplicate events are handled the same way. Your code should properly validate events and gracefully handle duplicate events. For more information, see [How do I make my Lambda function idempotent?](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-idempotent/).

# Deploy Node.js Lambda functions with .zip file archives
<a name="nodejs-package"></a>

 Your AWS Lambda function’s code comprises a .js or .mjs file containing your function’s handler code, together with any additional packages and modules your code depends on. To deploy this function code to Lambda, you use a *deployment package*. This package may either be a .zip file archive or a container image. For more information about using container images with Node.js, see [Deploy Node.js Lambda functions with container images](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-image.html). 

 To create your deployment package as .zip file archive, you can use your command-line tool’s built-in .zip file archive utility, or any other .zip file utility such as [7zip](https://www.7-zip.org/download.html). The examples shown in the following sections assume you’re using a command-line `zip` tool in a Linux or MacOS environment. To use the same commands in Windows, you can [install the Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10) to get a Windows-integrated version of Ubuntu and Bash. 

 Note that Lambda uses POSIX file permissions, so you may need to [set permissions for the deployment package folder](http://aws.amazon.com/premiumsupport/knowledge-center/lambda-deployment-package-errors/) before you create the .zip file archive. 

**Topics**
+ [Runtime dependencies in Node.js](#nodejs-package-dependencies)
+ [Creating a .zip deployment package with no dependencies](#nodejs-package-create-no-dependencies)
+ [Creating a .zip deployment package with dependencies](#nodejs-package-create-dependencies)
+ [Creating a Node.js layer for your dependencies](#nodejs-package-dependencies-layers)
+ [Dependency search path and runtime-included libraries](#nodejs-package-searchpath)
+ [Creating and updating Node.js Lambda functions using .zip files](#nodejs-package-create-update)

## Runtime dependencies in Node.js
<a name="nodejs-package-dependencies"></a>

 For Lambda functions that use the Node.js runtime, a dependency can be any Node.js module. The Node.js runtime includes a number of common libraries, as well as a version of the AWS SDK for JavaScript. All [supported Node.js runtimes](lambda-nodejs.md#nodejs-supported-runtimes) include version 3 of the SDK. To use version 2 of the SDK, you must add the SDK to your .zip file deployment package. To find the specific version of the SDK that's included in the runtime that you're using, see [Runtime-included SDK versions](lambda-nodejs.md#nodejs-sdk-included). 

 Lambda periodically updates the SDK libraries in the Node.js runtime to include the latest features and security upgrades. Lambda also applies security patches and updates to the other libraries included in the runtime. To have full control of the dependencies in your package, you can add your preferred version of any runtime-included dependency to your deployment package. For example, if you want to use a particular version of the SDK for JavaScript, you can include it in your .zip file as a dependency. For more information on adding runtime-included dependencies to your .zip file, see [Dependency search path and runtime-included libraries](#nodejs-package-searchpath). 

 Under the [AWS shared responsibility model](lambda-runtimes.md#runtimes-shared-responsibility), you are responsible for the management of any dependencies in your functions' deployment packages. This includes applying updates and security patches. To update dependencies in your function's deployment package, first create a new .zip file and then upload it to Lambda. See [Creating a .zip deployment package with dependencies](#nodejs-package-create-dependencies) and [Creating and updating Node.js Lambda functions using .zip files](#nodejs-package-create-update) for more information. 

## Creating a .zip deployment package with no dependencies
<a name="nodejs-package-create-no-dependencies"></a>

 If your function code has no dependencies except for libraries included in the Lambda runtime, your .zip file contains only the `index.js` or `index.mjs` file with your function’s handler code. Use your preferred zip utility to create a .zip file with your `index.js` or `index.mjs` file at the root. If the file containing your handler code isn't at the root of your .zip file, Lambda won’t be able to run your code. 

 To learn how to deploy your .zip file to create a new Lambda function or update an existing one, see [Creating and updating Node.js Lambda functions using .zip files](#nodejs-package-create-update). 

## Creating a .zip deployment package with dependencies
<a name="nodejs-package-create-dependencies"></a>

If your function code depends on packages or modules that aren't included in the Lambda Node.js runtime, you can either add these dependencies to your .zip file with your function code or use a [Lambda layer](chapter-layers.md). The instructions in this section show you how to include your dependencies in your .zip deployment package. For instructions on how to include your dependencies in a layer, see [Creating a Node.js layer for your dependencies](#nodejs-package-dependencies-layers).

The following example CLI commands create a .zip file named `my_deployment_package.zip` containing the `index.js` or `index.mjs` file with your function's handler code and its dependencies. In the example, you install dependencies using the npm package manager.

**To create the deployment package**

1. Navigate to the project directory containing your `index.js` or `index.mjs` source code file. In this example, the directory is named `my_function`.

   ```
   cd my_function
   ```

1. Install your function's required libraries in the `node_modules` directory using the `npm install` command. In this example you install the AWS X-Ray SDK for Node.js.

   ```
   npm install aws-xray-sdk
   ```

   This creates a folder structure similar to the following:

   ```
   ~/my_function
   ├── index.mjs
   └── node_modules
       ├── async
       ├── async-listener
       ├── atomic-batcher
       ├── aws-sdk
       ├── aws-xray-sdk
       ├── aws-xray-sdk-core
   ```

   You can also add custom modules that you create yourself to your deployment package. Create a directory under `node_modules` with the name of your module and save your custom written packages there.

1. Create a .zip file that contains the contents of your project folder at the root. Use the `r` (recursive) option to ensure that zip compresses the subfolders.

   ```
   zip -r my_deployment_package.zip .
   ```

## Creating a Node.js layer for your dependencies
<a name="nodejs-package-dependencies-layers"></a>

The instructions in this section show you how to include your dependencies in a layer. For instructions on how to include your dependencies in your deployment package, see [Creating a .zip deployment package with dependencies](#nodejs-package-create-dependencies).

When you add a layer to a function, Lambda loads the layer content into the `/opt` directory of that execution environment. For each Lambda runtime, the `PATH` variable already includes specific folder paths within the `/opt` directory. To ensure that Lambda picks up your layer content, your layer .zip file should have its dependencies in one of the following folder paths:
+ `nodejs/node_modules`
+ `nodejs/node18/node_modules (NODE_PATH)`
+ `nodejs/node20/node_modules (NODE_PATH)`
+ `nodejs/node22/node_modules (NODE_PATH)`

For example, your layer .zip file structure might look like the following:

```
xray-sdk.zip
└ nodejs/node_modules/aws-xray-sdk
```

In addition, Lambda automatically detects any libraries in the `/opt/lib` directory, and any binaries in the `/opt/bin` directory. To ensure that Lambda properly finds your layer content, you can also create a layer with the following structure:

```
custom-layer.zip
└ lib
    | lib_1
    | lib_2
└ bin
    | bin_1
    | bin_2
```

After you package your layer, see [Creating and deleting layers in Lambda](creating-deleting-layers.md) and [Adding layers to functions](adding-layers.md) to complete your layer setup.

## Dependency search path and runtime-included libraries
<a name="nodejs-package-searchpath"></a>

The Node.js runtime includes a number of common libraries, as well as a version of the AWS SDK for JavaScript. If you want to use a different version of a runtime-included library, you can do this by bundling it with your function or by adding it as a dependency in your deployment package. For example, you can use a different version of the SDK by adding it to your .zip deployment package. You can also include it in a [Lambda layer](chapter-layers.md) for your function.

When you use an `import` or `require` statement in your code, the Node.js runtime searches the directories in the `NODE_PATH` path until it finds the module. By default, the first location the runtime searches is the directory into which your .zip deployment package is decompressed and mounted (`/var/task`). If you include a version of a runtime-included library in your deployment package, this version will take precedence over the version included in the runtime. Dependencies in your deployment package also have precedence over dependencies in layers.

When you add a dependency to a layer, Lambda extracts this to `/opt/nodejs/nodexx/node_modules` where `nodexx` represents the version of the runtime you are using. In the search path, this directory has precedence over the directory containing the runtime-included libraries (`/var/lang/lib/node_modules`). Libraries in function layers therefore have precedence over versions included in the runtime.

You can see the full search path for your Lambda function by adding the following line of code.

```
console.log(process.env.NODE_PATH)
```

You can also add dependencies in a separate folder inside your .zip package. For example, you might add a custom module to a folder in your .zip package called `common`. When your .zip package is decompressed and mounted, this folder is placed inside the `/var/task` directory. To use a dependency from a folder in your .zip deployment package in your code, use an `import { } from` or `const { } = require()` statement, depending on whether you are using CJS or ESM module resolution. For example:

```
import { myModule } from './common'
```

If you bundle your code with `esbuild`, `rollup`, or similar, the dependencies used by your function are bundled together in one or more files. We recommend using this method to vend dependencies whenever possible. Compared to adding dependencies to your deployment package, bundling your code results in improved performance due to the reduction in I/O operations.

## Creating and updating Node.js Lambda functions using .zip files
<a name="nodejs-package-create-update"></a>

 After you have created your .zip deployment package, you can use it to create a new Lambda function or update an existing one. You can deploy your .zip package using the Lambda console, the AWS Command Line Interface, and the Lambda API. You can also create and update Lambda functions using AWS Serverless Application Model (AWS SAM) and CloudFormation. 

The maximum size for a .zip deployment package for Lambda is 250 MB (unzipped). Note that this limit applies to the combined size of all the files you upload, including any Lambda layers.

The Lambda runtime needs permission to read the files in your deployment package. In Linux permissions octal notation, Lambda needs 644 permissions for non-executable files (rw-r--r--) and 755 permissions (rwxr-xr-x) for directories and executable files.

In Linux and MacOS, use the `chmod` command to change file permissions on files and directories in your deployment package. For example, to give a non-executable file the correct permissions, run the following command.

```
chmod 644 <filepath>
```

To change file permissions in Windows, see [Set, View, Change, or Remove Permissions on an Object](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/cc731667(v=ws.10)) in the Microsoft Windows documentation.

**Note**  
If you don't grant Lambda the permissions it needs to access directories in your deployment package, Lambda sets the permissions for those directories to 755 (rwxr-xr-x).

### Creating and updating functions with .zip files using the console
<a name="nodejs-package-create-console"></a>

 To create a new function, you must first create the function in the console, then upload your .zip archive. To update an existing function, open the page for your function, then follow the same procedure to add your updated .zip file. 

 If your .zip file is less than 50MB, you can create or update a function by uploading the file directly from your local machine. For .zip files greater than 50MB, you must upload your package to an Amazon S3 bucket first. For instructions on how to upload a file to an Amazon S3 bucket using the AWS Management Console, see [Getting started with Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html). To upload files using the AWS CLI, see [Move objects](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-move) in the *AWS CLI User Guide*. 

**Note**  
You cannot change the [deployment package type](https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html#lambda-CreateFunction-request-PackageType) (.zip or container image) for an existing function. For example, you cannot convert a container image function to use a .zip file archive. You must create a new function.

**To create a new function (console)**

1. Open the [Functions page](https://console.aws.amazon.com/lambda/home#/functions) of the Lambda console and choose **Create Function**.

1. Choose **Author from scratch**.

1. Under **Basic information**, do the following:

   1. For **Function name**, enter the name for your function.

   1. For **Runtime**, select the runtime you want to use.

   1. (Optional) For **Architecture**, choose the instruction set architecture for your function. The default architecture is x86\$164. Ensure that the .zip deployment package for your function is compatible with the instruction set architecture you select.

1. (Optional) Under **Permissions**, expand **Change default execution role**. You can create a new **Execution role** or use an existing one.

1. Choose **Create function**. Lambda creates a basic 'Hello world' function using your chosen runtime.

**To upload a .zip archive from your local machine (console)**

1. In the [Functions page](https://console.aws.amazon.com/lambda/home#/functions) of the Lambda console, choose the function you want to upload the .zip file for.

1. Select the **Code** tab.

1. In the **Code source** pane, choose **Upload from**.

1. Choose **.zip file**.

1. To upload the .zip file, do the following:

   1. Select **Upload**, then select your .zip file in the file chooser.

   1. Choose **Open**.

   1. Choose **Save**.

**To upload a .zip archive from an Amazon S3 bucket (console)**

1. In the [Functions page](https://console.aws.amazon.com/lambda/home#/functions) of the Lambda console, choose the function you want to upload a new .zip file for.

1. Select the **Code** tab.

1. In the **Code source** pane, choose **Upload from**.

1. Choose **Amazon S3 location**.

1. Paste the Amazon S3 link URL of your .zip file and choose **Save**.

### Updating .zip file functions using the console code editor
<a name="nodejs-package-console-edit"></a>

 For some functions with .zip deployment packages, you can use the Lambda console’s built-in code editor to update your function code directly. To use this feature, your function must meet the following criteria: 
+ Your function must use one of the interpreted language runtimes (Python, Node.js, or Ruby)
+ Your function’s deployment package must be smaller than 50 MB (unzipped).

Function code for functions with container image deployment packages cannot be edited directly in the console.

**To update function code using the console code editor**

1. Open the [Functions page](https://console.aws.amazon.com/lambda/home#/functions) of the Lambda console and select your function.

1. Select the **Code** tab.

1. In the **Code source** pane, select your source code file and edit it in the integrated code editor.

1. In the **DEPLOY** section, choose **Deploy** to update your function's code:  
![\[\]](http://docs.aws.amazon.com/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

### Creating and updating functions with .zip files using the AWS CLI
<a name="nodejs-package-create-cli"></a>

 You can can use the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) to create a new function or to update an existing one using a .zip file. Use the [create-function](https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html) and [update-function-code](https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html) commands to deploy your .zip package. If your .zip file is smaller than 50MB, you can upload the .zip package from a file location on your local build machine. For larger files, you must upload your .zip package from an Amazon S3 bucket. For instructions on how to upload a file to an Amazon S3 bucket using the AWS CLI, see [Move objects](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-move) in the *AWS CLI User Guide*. 

**Note**  
If you upload your .zip file from an Amazon S3 bucket using the AWS CLI, the bucket must be located in the same AWS Region as your function.

 To create a new function using a .zip file with the AWS CLI, you must specify the following: 
+ The name of your function (`--function-name`)
+ Your function’s runtime (`--runtime`)
+ The Amazon Resource Name (ARN) of your function’s [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html) (`--role`)
+ The name of the handler method in your function code (`--handler`)

 You must also specify the location of your .zip file. If your .zip file is located in a folder on your local build machine, use the `--zip-file` option to specify the file path, as shown in the following example command. 

```
aws lambda create-function --function-name myFunction \
--runtime nodejs24.x --handler index.handler \
--role arn:aws:iam::111122223333:role/service-role/my-lambda-role \
--zip-file fileb://myFunction.zip
```

 To specify the location of .zip file in an Amazon S3 bucket, use the `--code` option as shown in the following example command. You only need to use the `S3ObjectVersion` parameter for versioned objects. 

```
aws lambda create-function --function-name myFunction \
--runtime nodejs24.x --handler index.handler \
--role arn:aws:iam::111122223333:role/service-role/my-lambda-role \
--code S3Bucket=amzn-s3-demo-bucket,S3Key=myFileName.zip,S3ObjectVersion=myObjectVersion
```

 To update an existing function using the CLI, you specify the the name of your function using the `--function-name` parameter. You must also specify the location of the .zip file you want to use to update your function code. If your .zip file is located in a folder on your local build machine, use the `--zip-file` option to specify the file path, as shown in the following example command. 

```
aws lambda update-function-code --function-name myFunction \
--zip-file fileb://myFunction.zip
```

 To specify the location of .zip file in an Amazon S3 bucket, use the `--s3-bucket` and `--s3-key` options as shown in the following example command. You only need to use the `--s3-object-version` parameter for versioned objects. 

```
aws lambda update-function-code --function-name myFunction \
--s3-bucket amzn-s3-demo-bucket --s3-key myFileName.zip --s3-object-version myObject Version
```

### Creating and updating functions with .zip files using the Lambda API
<a name="nodejs-package-create-api"></a>

 To create and update functions using a .zip file archive, use the following API operations: 
+ [CreateFunction](https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html)
+ [UpdateFunctionCode](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateFunctionCode.html)

### Creating and updating functions with .zip files using AWS SAM
<a name="nodejs-package-create-sam"></a>

 The AWS Serverless Application Model (AWS SAM) is a toolkit that helps streamline the process of building and running serverless applications on AWS. You define the resources for your application in a YAML or JSON template and use the AWS SAM command line interface (AWS SAM CLI) to build, package, and deploy your applications. When you build a Lambda function from an AWS SAM template, AWS SAM automatically creates a .zip deployment package or container image with your function code and any dependencies you specify. To learn more about using AWS SAM to build and deploy Lambda functions, see [Getting started with AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started.html) in the *AWS Serverless Application Model Developer Guide*.

You can also use AWS SAM to create a Lambda function using an existing .zip file archive. To create a Lambda function using AWS SAM, you can save your .zip file in an Amazon S3 bucket or in a local folder on your build machine. For instructions on how to upload a file to an Amazon S3 bucket using the AWS CLI, see [Move objects](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-move) in the *AWS CLI User Guide*. 

 In your AWS SAM template, the `AWS::Serverless::Function` resource specifies your Lambda function. In this resource, set the following properties to create a function using a .zip file archive: 
+ `PackageType` - set to `Zip`
+ `CodeUri` - set to the function code's Amazon S3 URI, path to local folder, or [FunctionCode](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-functioncode.html) object
+ `Runtime` - Set to your chosen runtime

 With AWS SAM, if your .zip file is larger than 50MB, you don’t need to upload it to an Amazon S3 bucket first. AWS SAM can upload .zip packages up to the maximum allowed size of 250MB (unzipped) from a location on your local build machine. 

 To learn more about deploying functions using .zip file in AWS SAM, see [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) in the *AWS SAM Developer Guide*. 

### Creating and updating functions with .zip files using CloudFormation
<a name="nodejs-package-create-cfn"></a>

 You can use CloudFormation to create a Lambda function using a .zip file archive. To create a Lambda function from a .zip file, you must first upload your file to an Amazon S3 bucket. For instructions on how to upload a file to an Amazon S3 bucket using the AWS CLI, see [Move objects](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-move) in the *AWS CLI User Guide. *

In your CloudFormation template, the `AWS::Lambda::Function` resource specifies your Lambda function. In this resource, set the following properties to create a function using a .zip file archive:
+ `PackageType` - Set to `Zip`
+ `Code` - Enter the Amazon S3 bucket name and the .zip file name in the `S3Bucket` and `S3Key` fields
+ `Runtime` - Set to your chosen runtime

 The .zip file that CloudFormation generates cannot exceed 4MB. To learn more about deploying functions using .zip file in CloudFormation, see [AWS::Lambda::Function](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html) in the *CloudFormation User Guide*. 

# Deploy Node.js Lambda functions with container images
<a name="nodejs-image"></a>

There are three ways to build a container image for a Node.js Lambda function:
+ [Using an AWS base image for Node.js](#nodejs-image-instructions)

  The [AWS base images](images-create.md#runtimes-images-lp) are preloaded with a language runtime, a runtime interface client to manage the interaction between Lambda and your function code, and a runtime interface emulator for local testing.
+ [Using an AWS OS-only base image](images-create.md#runtimes-images-provided)

  [AWS OS-only base images](https://gallery.ecr.aws/lambda/provided) contain an Amazon Linux distribution and the [runtime interface emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator/). These images are commonly used to create container images for compiled languages, such as [Go](go-image.md#go-image-provided) and [Rust](lambda-rust.md), and for a language or language version that Lambda doesn't provide a base image for, such as Node.js 19. You can also use OS-only base images to implement a [custom runtime](runtimes-custom.md). To make the image compatible with Lambda, you must include the [runtime interface client for Node.js](#nodejs-image-clients) in the image.
+ [Using a non-AWS base image](#nodejs-image-clients)

  You can use an alternative base image from another container registry, such as Alpine Linux or Debian. You can also use a custom image created by your organization. To make the image compatible with Lambda, you must include the [runtime interface client for Node.js](#nodejs-image-clients) in the image.

**Tip**  
To reduce the time it takes for Lambda container functions to become active, see [Use multi-stage builds](https://docs.docker.com/build/building/multi-stage/) in the Docker documentation. To build efficient container images, follow the [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/).

This page explains how to build, test, and deploy container images for Lambda.

**Topics**
+ [AWS base images for Node.js](#nodejs-image-base)
+ [Using an AWS base image for Node.js](#nodejs-image-instructions)
+ [Using an alternative base image with the runtime interface client](#nodejs-image-clients)

## AWS base images for Node.js
<a name="nodejs-image-base"></a>

AWS provides the following base images for Node.js:


| Tags | Runtime | Operating system | Dockerfile | Deprecation | 
| --- | --- | --- | --- | --- | 
| 24 | Node.js 24 | Amazon Linux 2023 | [Dockerfile for Node.js 24 on GitHub](https://github.com/aws/aws-lambda-base-images/blob/nodejs24.x/Dockerfile.nodejs24.x) |   Apr 30, 2028   | 
| 22 | Node.js 22 | Amazon Linux 2023 | [Dockerfile for Node.js 22 on GitHub](https://github.com/aws/aws-lambda-base-images/blob/nodejs22.x/Dockerfile.nodejs22.x) |   Apr 30, 2027   | 
| 20 | Node.js 20 | Amazon Linux 2023 | [Dockerfile for Node.js 20 on GitHub](https://github.com/aws/aws-lambda-base-images/blob/nodejs20.x/Dockerfile.nodejs20.x) |   Apr 30, 2026   | 

Amazon ECR repository: [gallery.ecr.aws/lambda/nodejs](https://gallery.ecr.aws/lambda/nodejs)

The Node.js 20 and later base images are based on the [Amazon Linux 2023 minimal container image](https://docs.aws.amazon.com/linux/al2023/ug/minimal-container.html). Earlier base images use Amazon Linux 2. AL2023 provides several advantages over Amazon Linux 2, including a smaller deployment footprint and updated versions of libraries such as `glibc`.

AL2023-based images use `microdnf` (symlinked as `dnf`) as the package manager instead of `yum`, which is the default package manager in Amazon Linux 2. `microdnf` is a standalone implementation of `dnf`. For a list of packages that are included in AL2023-based images, refer to the **Minimal Container** columns in [Comparing packages installed on Amazon Linux 2023 Container Images](https://docs.aws.amazon.com/linux/al2023/ug/al2023-container-image-types.html). For more information about the differences between AL2023 and Amazon Linux 2, see [Introducing the Amazon Linux 2023 runtime for AWS Lambda](https://aws.amazon.com/blogs/compute/introducing-the-amazon-linux-2023-runtime-for-aws-lambda/) on the AWS Compute Blog.

**Note**  
To run AL2023-based images locally, including with AWS Serverless Application Model (AWS SAM), you must use Docker version 20.10.10 or later.

## Using an AWS base image for Node.js
<a name="nodejs-image-instructions"></a>

### Prerequisites
<a name="nodejs-image-prerequisites"></a>

To complete the steps in this section, you must have the following:
+ [AWS CLI version 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [Docker](https://docs.docker.com/get-docker) (minimum version 25.0.0)
+ The Docker [buildx plugin](https://github.com/docker/buildx/blob/master/README.md).
+ Node.js

### Creating an image from a base image
<a name="nodejs-image-create"></a>

**To create a container image from an AWS base image for Node.js**

1. Create a directory for the project, and then switch to that directory.

   ```
   mkdir example
   cd example
   ```

1. Create a new Node.js project with `npm`. To accept the default options provided in the interactive experience, press `Enter`.

   ```
   npm init
   ```

1. Create a new file called `index.js`. You can add the following sample function code to the file for testing, or use your own.  
**Example CommonJS handler**  

   ```
   exports.handler = async (event) => {
       const response = {
           statusCode: 200,
           body: JSON.stringify('Hello from Lambda!'),
       };
       return response;
   };
   ```

1. If your function depends on libraries other than the AWS SDK for JavaScript, use [npm](https://www.npmjs.com/) to add them to your package.

1. Create a new Dockerfile with the following configuration:
   + Set the `FROM` property to the [URI of the base image](https://gallery.ecr.aws/lambda/nodejs).
   + Use the COPY command to copy the function code and runtime dependencies to `{LAMBDA_TASK_ROOT}`, a [Lambda-defined environment variable](configuration-envvars.md#configuration-envvars-runtime).
   + Set the `CMD` argument to the Lambda function handler.

   Note that the example Dockerfile does not include a [USER instruction](https://docs.docker.com/reference/dockerfile/#user). When you deploy a container image to Lambda, Lambda automatically defines a default Linux user with least-privileged permissions. This is different from standard Docker behavior which defaults to the `root` user when no `USER` instruction is provided.  
**Example Dockerfile**  

   ```
   FROM public.ecr.aws/lambda/nodejs:22
   
   # Copy function code
   COPY index.js ${LAMBDA_TASK_ROOT}
     
   # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
   CMD [ "index.handler" ]
   ```

1. Build the Docker image with the [docker build](https://docs.docker.com/engine/reference/commandline/build/) command. The following example names the image `docker-image` and gives it the `test` [tag](https://docs.docker.com/engine/reference/commandline/build/#tag). To make your image compatible with Lambda, you must use the `--provenance=false` option.

   ```
   docker buildx build --platform linux/amd64 --provenance=false -t docker-image:test .
   ```
**Note**  
The command specifies the `--platform linux/amd64` option to ensure that your container is compatible with the Lambda execution environment regardless of the architecture of your build machine. If you intend to create a Lambda function using the ARM64 instruction set architecture, be sure to change the command to use the `--platform linux/arm64` option instead.

### (Optional) Test the image locally
<a name="nodejs-image-test"></a>

1. Start the Docker image with the **docker run** command. In this example, `docker-image` is the image name and `test` is the tag.

   ```
   docker run --platform linux/amd64 -p 9000:8080 docker-image:test
   ```

   This command runs the image as a container and creates a local endpoint at `localhost:9000/2015-03-31/functions/function/invocations`.
**Note**  
If you built the Docker image for the ARM64 instruction set architecture, be sure to use the `--platform linux/arm64` option instead of `--platform linux/amd64`.

1. From a new terminal window, post an event to the local endpoint.

------
#### [ Linux/macOS ]

   In Linux and macOS, run the following `curl` command:

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
   ```

   This command invokes the function with an empty event and returns a response. If you're using your own function code rather than the sample function code, you might want to invoke the function with a JSON payload. Example:

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
   ```

------
#### [ PowerShell ]

   In PowerShell, run the following `Invoke-WebRequest` command:

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{}' -ContentType "application/json"
   ```

   This command invokes the function with an empty event and returns a response. If you're using your own function code rather than the sample function code, you might want to invoke the function with a JSON payload. Example:

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{"payload":"hello world!"}' -ContentType "application/json"
   ```

------

1. Get the container ID.

   ```
   docker ps
   ```

1. Use the [docker kill](https://docs.docker.com/engine/reference/commandline/kill/) command to stop the container. In this command, replace `3766c4ab331c` with the container ID from the previous step.

   ```
   docker kill 3766c4ab331c
   ```

### Deploying the image
<a name="nodejs-image-deploy"></a>

**To upload the image to Amazon ECR and create the Lambda function**

1. Run the [get-login-password](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/get-login-password.html) command to authenticate the Docker CLI to your Amazon ECR registry.
   + Set the `--region` value to the AWS Region where you want to create the Amazon ECR repository.
   + Replace `111122223333` with your AWS account ID.

   ```
   aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
   ```

1. Create a repository in Amazon ECR using the [create-repository](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/create-repository.html) command.

   ```
   aws ecr create-repository --repository-name hello-world --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
   ```
**Note**  
The Amazon ECR repository must be in the same AWS Region as the Lambda function.

   If successful, you see a response like this:

   ```
   {
       "repository": {
           "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world",
           "registryId": "111122223333",
           "repositoryName": "hello-world",
           "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world",
           "createdAt": "2023-03-09T10:39:01+00:00",
           "imageTagMutability": "MUTABLE",
           "imageScanningConfiguration": {
               "scanOnPush": true
           },
           "encryptionConfiguration": {
               "encryptionType": "AES256"
           }
       }
   }
   ```

1. Copy the `repositoryUri` from the output in the previous step.

1. Run the [docker tag](https://docs.docker.com/engine/reference/commandline/tag/) command to tag your local image into your Amazon ECR repository as the latest version. In this command:
   + `docker-image:test` is the name and [tag](https://docs.docker.com/engine/reference/commandline/build/#tag) of your Docker image. This is the image name and tag that you specified in the `docker build` command.
   + Replace `<ECRrepositoryUri>` with the `repositoryUri` that you copied. Make sure to include `:latest` at the end of the URI.

   ```
   docker tag docker-image:test <ECRrepositoryUri>:latest
   ```

   Example:

   ```
   docker tag docker-image:test 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. Run the [docker push](https://docs.docker.com/engine/reference/commandline/push/) command to deploy your local image to the Amazon ECR repository. Make sure to include `:latest` at the end of the repository URI.

   ```
   docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. [Create an execution role](lambda-intro-execution-role.md#permissions-executionrole-api) for the function, if you don't already have one. You need the Amazon Resource Name (ARN) of the role in the next step.

1. Create the Lambda function. For `ImageUri`, specify the repository URI from earlier. Make sure to include `:latest` at the end of the URI.

   ```
   aws lambda create-function \
     --function-name hello-world \
     --package-type Image \
     --code ImageUri=111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
     --role arn:aws:iam::111122223333:role/lambda-ex
   ```
**Note**  
You can create a function using an image in a different AWS account, as long as the image is in the same Region as the Lambda function. For more information, see [Amazon ECR cross-account permissions](images-create.md#configuration-images-xaccount-permissions).

1. Invoke the function.

   ```
   aws lambda invoke --function-name hello-world response.json
   ```

   You should see a response like this:

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

1. To see the output of the function, check the `response.json` file.

To update the function code, you must build the image again, upload the new image to the Amazon ECR repository, and then use the [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) command to deploy the image to the Lambda function.

Lambda resolves the image tag to a specific image digest. This means that if you point the image tag that was used to deploy the function to a new image in Amazon ECR, Lambda doesn't automatically update the function to use the new image.

To deploy the new image to the same Lambda function, you must use the [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) command, even if the image tag in Amazon ECR remains the same. In the following example, the `--publish` option creates a new version of the function using the updated container image.

```
aws lambda update-function-code \
  --function-name hello-world \
  --image-uri 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
  --publish
```

## Using an alternative base image with the runtime interface client
<a name="nodejs-image-clients"></a>

If you use an [OS-only base image](images-create.md#runtimes-images-provided) or an alternative base image, you must include the runtime interface client in your image. The runtime interface client extends the [Runtime API](runtimes-api.md), which manages the interaction between Lambda and your function code.

Install the [Node.js runtime interface client](https://www.npmjs.com/package/aws-lambda-ric) using the npm package manager:

```
npm install aws-lambda-ric
```

You can also download the [Node.js runtime interface client](https://github.com/aws/aws-lambda-nodejs-runtime-interface-client) from GitHub.

The following example demonstrates how to build a container image for Node.js using a non-AWS base image. The example Dockerfile uses a `bookworm` base image. The Dockerfile includes the runtime interface client.

### Prerequisites
<a name="nodejs-alt-prerequisites"></a>

To complete the steps in this section, you must have the following:
+ [AWS CLI version 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+ [Docker](https://docs.docker.com/get-docker) (minimum version 25.0.0)
+ The Docker [buildx plugin](https://github.com/docker/buildx/blob/master/README.md).
+ Node.js

### Creating an image from an alternative base image
<a name="nodejs-alt-create"></a>

**To create a container image from a non-AWS base image**

1. Create a directory for the project, and then switch to that directory.

   ```
   mkdir example
   cd example
   ```

1. Create a new Node.js project with `npm`. To accept the default options provided in the interactive experience, press `Enter`.

   ```
   npm init
   ```

1. Create a new file called `index.js`. You can add the following sample function code to the file for testing, or use your own.  
**Example CommonJS handler**  

   ```
   exports.handler = async (event) => {
       const response = {
           statusCode: 200,
           body: JSON.stringify('Hello from Lambda!'),
       };
       return response;
   };
   ```

1. Create a new Dockerfile. The following Dockerfile uses a `bookworm` base image instead of an [AWS base image](images-create.md#runtimes-images-lp). The Dockerfile includes the [runtime interface client](https://www.npmjs.com/package/aws-lambda-ric), which makes the image compatible with Lambda. The Dockerfile uses a [multi-stage build](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#use-multi-stage-builds). The first stage creates a build image, which is a standard Node.js environment where the function's dependencies are installed. The second stage creates a slimmer image which includes the function code and its dependencies. This reduces the final image size.
   + Set the `FROM` property to the base image identifier.
   + Use the `COPY` command to copy the function code and runtime dependencies.
   + Set the `ENTRYPOINT` to the module that you want the Docker container to run when it starts. In this case, the module is the runtime interface client.
   + Set the `CMD` argument to the Lambda function handler.

   Note that the example Dockerfile does not include a [USER instruction](https://docs.docker.com/reference/dockerfile/#user). When you deploy a container image to Lambda, Lambda automatically defines a default Linux user with least-privileged permissions. This is different from standard Docker behavior which defaults to the `root` user when no `USER` instruction is provided.  
**Example Dockerfile**  

   ```
   # Define custom function directory
   ARG FUNCTION_DIR="/function"
   
   FROM node:20-bookworm as build-image
   
   # Include global arg in this stage of the build
   ARG FUNCTION_DIR
   
   # Install build dependencies
   RUN apt-get update && \
       apt-get install -y \
       g++ \
       make \
       cmake \
       unzip \
       libcurl4-openssl-dev
   
   # Copy function code
   RUN mkdir -p ${FUNCTION_DIR}
   COPY . ${FUNCTION_DIR}
   
   WORKDIR ${FUNCTION_DIR}
   
   # Install Node.js dependencies
   RUN npm install
   
   # Install the runtime interface client
   RUN npm install aws-lambda-ric
   
   # Grab a fresh slim copy of the image to reduce the final size
   FROM node:20-bookworm-slim
   
   # Required for Node runtimes which use npm@8.6.0+ because
   # by default npm writes logs under /home/.npm and Lambda fs is read-only
   ENV NPM_CONFIG_CACHE=/tmp/.npm
   
   # Include global arg in this stage of the build
   ARG FUNCTION_DIR
   
   # Set working directory to function root directory
   WORKDIR ${FUNCTION_DIR}
   
   # Copy in the built dependencies
   COPY --from=build-image ${FUNCTION_DIR} ${FUNCTION_DIR}
   
   # Set runtime interface client as default command for the container runtime
   ENTRYPOINT ["/usr/local/bin/npx", "aws-lambda-ric"]
   # Pass the name of the function handler as an argument to the runtime
   CMD ["index.handler"]
   ```

1. Build the Docker image with the [docker build](https://docs.docker.com/engine/reference/commandline/build/) command. The following example names the image `docker-image` and gives it the `test` [tag](https://docs.docker.com/engine/reference/commandline/build/#tag). To make your image compatible with Lambda, you must use the `--provenance=false` option.

   ```
   docker buildx build --platform linux/amd64 --provenance=false -t docker-image:test .
   ```
**Note**  
The command specifies the `--platform linux/amd64` option to ensure that your container is compatible with the Lambda execution environment regardless of the architecture of your build machine. If you intend to create a Lambda function using the ARM64 instruction set architecture, be sure to change the command to use the `--platform linux/arm64` option instead.

### (Optional) Test the image locally
<a name="nodejs-alt-test"></a>

Use the [runtime interface emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator/) to locally test the image. You can [build the emulator into your image](https://github.com/aws/aws-lambda-runtime-interface-emulator/?tab=readme-ov-file#build-rie-into-your-base-image) or use the following procedure to install it on your local machine.

**To install and run the runtime interface emulator on your local machine**

1. From your project directory, run the following command to download the runtime interface emulator (x86-64 architecture) from GitHub and install it on your local machine.

------
#### [ Linux/macOS ]

   ```
   mkdir -p ~/.aws-lambda-rie && \
       curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && \
       chmod +x ~/.aws-lambda-rie/aws-lambda-rie
   ```

   To install the arm64 emulator, replace the GitHub repository URL in the previous command with the following:

   ```
   https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64
   ```

------
#### [ PowerShell ]

   ```
   $dirPath = "$HOME\.aws-lambda-rie"
   if (-not (Test-Path $dirPath)) {
       New-Item -Path $dirPath -ItemType Directory
   }
         
   $downloadLink = "https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie"
   $destinationPath = "$HOME\.aws-lambda-rie\aws-lambda-rie"
   Invoke-WebRequest -Uri $downloadLink -OutFile $destinationPath
   ```

   To install the arm64 emulator, replace the `$downloadLink` with the following:

   ```
   https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64
   ```

------

1. Start the Docker image with the **docker run** command. Note the following:
   + `docker-image` is the image name and `test` is the tag.
   + `/usr/local/bin/npx aws-lambda-ric index.handler` is the `ENTRYPOINT` followed by the `CMD` from your Dockerfile.

------
#### [ Linux/macOS ]

   ```
   docker run --platform linux/amd64 -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
       --entrypoint /aws-lambda/aws-lambda-rie \
       docker-image:test \
           /usr/local/bin/npx aws-lambda-ric index.handler
   ```

------
#### [ PowerShell ]

   ```
   docker run --platform linux/amd64 -d -v "$HOME\.aws-lambda-rie:/aws-lambda" -p 9000:8080 `
   --entrypoint /aws-lambda/aws-lambda-rie `
   docker-image:test `
       /usr/local/bin/npx aws-lambda-ric index.handler
   ```

------

   This command runs the image as a container and creates a local endpoint at `localhost:9000/2015-03-31/functions/function/invocations`.
**Note**  
If you built the Docker image for the ARM64 instruction set architecture, be sure to use the `--platform linux/arm64` option instead of `--platform linux/amd64`.

1. Post an event to the local endpoint.

------
#### [ Linux/macOS ]

   In Linux and macOS, run the following `curl` command:

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
   ```

   This command invokes the function with an empty event and returns a response. If you're using your own function code rather than the sample function code, you might want to invoke the function with a JSON payload. Example:

   ```
   curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
   ```

------
#### [ PowerShell ]

   In PowerShell, run the following `Invoke-WebRequest` command:

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{}' -ContentType "application/json"
   ```

   This command invokes the function with an empty event and returns a response. If you're using your own function code rather than the sample function code, you might want to invoke the function with a JSON payload. Example:

   ```
   Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{"payload":"hello world!"}' -ContentType "application/json"
   ```

------

1. Get the container ID.

   ```
   docker ps
   ```

1. Use the [docker kill](https://docs.docker.com/engine/reference/commandline/kill/) command to stop the container. In this command, replace `3766c4ab331c` with the container ID from the previous step.

   ```
   docker kill 3766c4ab331c
   ```

### Deploying the image
<a name="nodejs-alt-deploy"></a>

**To upload the image to Amazon ECR and create the Lambda function**

1. Run the [get-login-password](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/get-login-password.html) command to authenticate the Docker CLI to your Amazon ECR registry.
   + Set the `--region` value to the AWS Region where you want to create the Amazon ECR repository.
   + Replace `111122223333` with your AWS account ID.

   ```
   aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
   ```

1. Create a repository in Amazon ECR using the [create-repository](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/create-repository.html) command.

   ```
   aws ecr create-repository --repository-name hello-world --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
   ```
**Note**  
The Amazon ECR repository must be in the same AWS Region as the Lambda function.

   If successful, you see a response like this:

   ```
   {
       "repository": {
           "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world",
           "registryId": "111122223333",
           "repositoryName": "hello-world",
           "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world",
           "createdAt": "2023-03-09T10:39:01+00:00",
           "imageTagMutability": "MUTABLE",
           "imageScanningConfiguration": {
               "scanOnPush": true
           },
           "encryptionConfiguration": {
               "encryptionType": "AES256"
           }
       }
   }
   ```

1. Copy the `repositoryUri` from the output in the previous step.

1. Run the [docker tag](https://docs.docker.com/engine/reference/commandline/tag/) command to tag your local image into your Amazon ECR repository as the latest version. In this command:
   + `docker-image:test` is the name and [tag](https://docs.docker.com/engine/reference/commandline/build/#tag) of your Docker image. This is the image name and tag that you specified in the `docker build` command.
   + Replace `<ECRrepositoryUri>` with the `repositoryUri` that you copied. Make sure to include `:latest` at the end of the URI.

   ```
   docker tag docker-image:test <ECRrepositoryUri>:latest
   ```

   Example:

   ```
   docker tag docker-image:test 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. Run the [docker push](https://docs.docker.com/engine/reference/commandline/push/) command to deploy your local image to the Amazon ECR repository. Make sure to include `:latest` at the end of the repository URI.

   ```
   docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
   ```

1. [Create an execution role](lambda-intro-execution-role.md#permissions-executionrole-api) for the function, if you don't already have one. You need the Amazon Resource Name (ARN) of the role in the next step.

1. Create the Lambda function. For `ImageUri`, specify the repository URI from earlier. Make sure to include `:latest` at the end of the URI.

   ```
   aws lambda create-function \
     --function-name hello-world \
     --package-type Image \
     --code ImageUri=111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
     --role arn:aws:iam::111122223333:role/lambda-ex
   ```
**Note**  
You can create a function using an image in a different AWS account, as long as the image is in the same Region as the Lambda function. For more information, see [Amazon ECR cross-account permissions](images-create.md#configuration-images-xaccount-permissions).

1. Invoke the function.

   ```
   aws lambda invoke --function-name hello-world response.json
   ```

   You should see a response like this:

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

1. To see the output of the function, check the `response.json` file.

To update the function code, you must build the image again, upload the new image to the Amazon ECR repository, and then use the [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) command to deploy the image to the Lambda function.

Lambda resolves the image tag to a specific image digest. This means that if you point the image tag that was used to deploy the function to a new image in Amazon ECR, Lambda doesn't automatically update the function to use the new image.

To deploy the new image to the same Lambda function, you must use the [update-function-code](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-code.html) command, even if the image tag in Amazon ECR remains the same. In the following example, the `--publish` option creates a new version of the function using the updated container image.

```
aws lambda update-function-code \
  --function-name hello-world \
  --image-uri 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \
  --publish
```

# Working with layers for Node.js Lambda functions
<a name="nodejs-layers"></a>

Use [Lambda layers](chapter-layers.md) to package code and dependencies that you want to reuse across multiple functions. Layers usually contain library dependencies, a [custom runtime](runtimes-custom.md), 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.

1. Create the layer in Lambda.

1. Add the layer to your functions.

**Topics**
+ [Package your layer content](#nodejs-layers-package)
+ [Create the layer in Lambda](#publishing-layer)
+ [Add the layer to your function](#nodejs-layer-adding)
+ [Sample app](#nodejs-layer-sample-app)

## Package your layer content
<a name="nodejs-layers-package"></a>

To create a layer, bundle your packages into a .zip file archive that meets the following requirements:
+ Build the layer using the same Node.js version that you plan to use for the Lambda function. For example, if you build your layer using Node.js 24, use the Node.js 24 runtime for your function.
+ Your layer's .zip file must use one of these directory structures:
  + `nodejs/node_modules`
  + `nodejs/nodeX/node_modules` (where *X* is your Node.js version, for example `node22`)

  For more information, see [Layer paths for each Lambda runtime](packaging-layers.md#packaging-layers-paths).
+ The packages in your layer must be compatible with Linux. Lambda functions run on Amazon Linux.

You can create layers that contain either third-party Node.js libraries installed with `npm` (such as `axios` or `lodash`) or your own JavaScript modules.

### Third-party dependencies
<a name="nodejs-layers-third-party-dependencies"></a>

**To create a layer using npm packages**

1. Create the required directory structure and install packages directly into it:

   ```
   mkdir -p nodejs
   npm install --prefix nodejs lodash axios
   ```

   This command installs the packages directly into the `nodejs/node_modules` directory, which is the structure that Lambda requires.
**Note**  
For packages with native dependencies or binary components (such as [sharp](https://www.npmjs.com/package/sharp) or [bcrypt](https://www.npmjs.com/package/bcrypt)), ensure that they're compatible with the Lambda Linux environment and your function's [architecture](foundation-arch.md). You might need to use the `--platform` flag:  

   ```
   npm install --prefix nodejs --platform=linux --arch=x64 sharp
   ```
For more complex native dependencies, you might need to compile them in a Linux environment that matches the Lambda runtime. You can use Docker for this purpose.

1. Zip the layer content:

------
#### [ Linux/macOS ]

   ```
   zip -r layer.zip nodejs/
   ```

------
#### [ PowerShell ]

   ```
   Compress-Archive -Path .\nodejs -DestinationPath .\layer.zip
   ```

------

   The directory structure of your .zip file should look like this:

   ```
   nodejs/
   ├── package.json
   ├── package-lock.json
   └── node_modules/
       ├── lodash/
       ├── axios/
       └── (dependencies of the other packages)
   ```
**Note**  
Make sure your .zip file includes the `nodejs` directory at the root level with `node_modules` inside it. This structure ensures that Lambda can locate and import your packages.
The `package.json` and `package-lock.json` files in the `nodejs/` directory are used by npm for dependency management but are not required by Lambda for layer functionality. Each installed package already contains its own `package.json` file that defines how Lambda imports the package.

### Custom JavaScript modules
<a name="custom-nodejs-modules"></a>

**To create a layer using your own code**

1. Create the required directory structure for your layer:

   ```
   mkdir -p nodejs/node_modules/validator
   cd nodejs/node_modules/validator
   ```

1. Create a `package.json` file for your custom module to define how it should be imported:  
**Example nodejs/node\$1modules/validator/package.json**  

   ```
   {
     "name": "validator",
     "version": "1.0.0",
     "type": "module",
     "main": "index.mjs"
   }
   ```

1. Create your JavaScript module file:  
**Example nodejs/node\$1modules/validator/index.mjs**  

   ```
   export function validateOrder(orderData) {
     // Validates an order and returns formatted data
     const requiredFields = ['productId', 'quantity'];
     
     // Check required fields
     const missingFields = requiredFields.filter(field => !(field in orderData));
     if (missingFields.length > 0) {
       throw new Error(`Missing required fields: ${missingFields.join(', ')}`);
     }
     
     // Validate quantity
     const quantity = orderData.quantity;
     if (!Number.isInteger(quantity) || quantity < 1) {
       throw new Error('Quantity must be a positive integer');
     }
     
     // Format and return the validated data
     return {
       productId: String(orderData.productId),
       quantity: quantity,
       shippingPriority: orderData.priority || 'standard'
     };
   }
   
   export function formatResponse(statusCode, body) {
     // Formats the API response
     return {
       statusCode: statusCode,
       body: JSON.stringify(body)
     };
   }
   ```

1. Zip the layer content:

------
#### [ Linux/macOS ]

   ```
   zip -r layer.zip nodejs/
   ```

------
#### [ PowerShell ]

   ```
   Compress-Archive -Path .\nodejs -DestinationPath .\layer.zip
   ```

------

   The directory structure of your .zip file should look like this:

   ```
   nodejs/              
   └── node_modules/
       └── validator/
           ├── package.json
           └── index.mjs
   ```

1. In your function, import and use the modules. Example:

   ```
   import { validateOrder, formatResponse } from 'validator';
   
   export const handler = async (event) => {
     try {
       // Parse the order data from the event body
       const orderData = JSON.parse(event.body || '{}');
       
       // Validate and format the order
       const validatedOrder = validateOrder(orderData);
       
       return formatResponse(200, {
         message: 'Order validated successfully',
         order: validatedOrder
       });
     } catch (error) {
       if (error instanceof Error && error.message.includes('Missing required fields')) {
         return formatResponse(400, {
           error: error.message
         });
       }
       
       return formatResponse(500, {
         error: 'Internal server error'
       });
     }
   };
   ```

   You can use the following [test event](testing-functions.md#invoke-with-event) to invoke the function:

   ```
   {
       "body": "{\"productId\": \"ABC123\", \"quantity\": 2, \"priority\": \"express\"}"
   }
   ```

   Expected response:

   ```
   {
     "statusCode": 200,
     "body": "{\"message\":\"Order validated successfully\",\"order\":{\"productId\":\"ABC123\",\"quantity\":2,\"shippingPriority\":\"express\"}}"
   }
   ```

## Create the layer in Lambda
<a name="publishing-layer"></a>

You can publish your layer using either the AWS CLI or the Lambda console.

------
#### [ AWS CLI ]

Run the [publish-layer-version](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/publish-layer-version.html) AWS CLI command to create the Lambda layer:

```
aws lambda publish-layer-version --layer-name my-layer --zip-file fileb://layer.zip --compatible-runtimes nodejs24.x
```

The [compatible runtimes](https://docs.aws.amazon.com/lambda/latest/api/API_PublishLayerVersion.html#lambda-PublishLayerVersion-request-CompatibleRuntimes) parameter is optional. When specified, Lambda uses this parameter to filter layers in the Lambda console.

------
#### [ Console ]

**To create a layer (console)**

1. Open the [Layers page](https://console.aws.amazon.com/lambda/home#/layers) of the Lambda console.

1. Choose **Create layer**.

1. Choose **Upload a .zip file**, and then upload the .zip archive that you created earlier.

1. (Optional) For **Compatible runtimes**, choose the Node.js runtime that corresponds to the Node.js version you used to build your layer.

1. Choose **Create**.

------

## Add the layer to your function
<a name="nodejs-layer-adding"></a>

------
#### [ AWS CLI ]

To attach the layer to your function, run the [update-function-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-configuration.html) AWS CLI command. For the `--layers` parameter, use the layer ARN. The ARN must specify the version (for example, `arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1`). For more information, see [Layers and layer versions](chapter-layers.md#lambda-layer-versions).

```
aws lambda update-function-configuration --function-name my-function --cli-binary-format raw-in-base64-out --layers "arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1"
```

The **cli-binary-format** option is required if you're using AWS CLI version 2. To make this the default setting, run `aws configure set cli-binary-format raw-in-base64-out`. For more information, see [AWS CLI supported global command line options](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list) in the *AWS Command Line Interface User Guide for Version 2*.

------
#### [ Console ]

**To add a layer to a function**

1. Open the [Functions page](https://console.aws.amazon.com/lambda/home#/functions) of the Lambda console.

1. Choose the function.

1. Scroll down to the **Layers** section, and then choose **Add a layer**.

1. Under **Choose a layer**, select **Custom layers**, and then choose your layer.
**Note**  
If you didn't add a [compatible runtime](https://docs.aws.amazon.com/lambda/latest/api/API_PublishLayerVersion.html#lambda-PublishLayerVersion-request-CompatibleRuntimes) when you created the layer, your layer won't be listed here. You can specify the layer ARN instead.

1. Choose **Add**.

------

## Sample app
<a name="nodejs-layer-sample-app"></a>

For more examples of how to use Lambda layers, see the [layer-nodejs](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/layer-nodejs) sample application in the AWS Lambda Developer Guide GitHub repository. This application includes a layer that contains the [lodash](https://www.npmjs.com/package/lodash) library. After creating the layer, you can deploy and invoke the corresponding function to confirm that the layer works as expected.

# Using the Lambda context object to retrieve Node.js function information
<a name="nodejs-context"></a>

When Lambda runs your function, it passes a context object to the [handler](nodejs-handler.md). This object provides methods and properties that provide information about the invocation, function, and execution environment.

**Context methods**
+ `getRemainingTimeInMillis()` – Returns the number of milliseconds left before the execution times out.

**Context properties**
+ `functionName` – The name of the Lambda function.
+ `functionVersion` – The [version](configuration-versions.md) of the function.
+ `invokedFunctionArn` – The Amazon Resource Name (ARN) that's used to invoke the function. Indicates if the invoker specified a version number or alias.
+ `memoryLimitInMB` – The amount of memory that's allocated for the function.
+ `awsRequestId` – The identifier of the invocation request.
+ `logGroupName` – The log group for the function.
+ `logStreamName` – The log stream for the function instance.
+ `identity` – (mobile apps) Information about the Amazon Cognito identity that authorized the request.
  + `cognitoIdentityId` – The authenticated Amazon Cognito identity.
  + `cognitoIdentityPoolId` – The Amazon Cognito identity pool that authorized the invocation.
+ `clientContext` – (mobile apps) Client context that's provided to Lambda by the client application.
  + `client.installation_id`
  + `client.app_title`
  + `client.app_version_name`
  + `client.app_version_code`
  + `client.app_package_name`
  + `env.platform_version`
  + `env.platform`
  + `env.make`
  + `env.model`
  + `env.locale`
  + `custom` – Custom values that are set by the client application. 
+ `callbackWaitsForEmptyEventLoop` – By default (`true`), when using a callback-based function handler, Lambda waits for the event loop to be empty after the callback runs before ending the function invoke. Set to `false` to send the response and end the invoke immediately after the callback runs instead of waiting for the event loop to be empty. Outstanding events continue to run during the next invocation. Note that Lambda supports callback-based function handlers for Node.js 22 and earlier runtimes only.

The following example function logs context information and returns the location of the logs.

**Example index.js file**  

```
exports.handler = async function(event, context) {
  console.log('Remaining time: ', context.getRemainingTimeInMillis())
  console.log('Function name: ', context.functionName)
  return context.logStreamName
}
```

# Log and monitor Node.js Lambda functions
<a name="nodejs-logging"></a>

AWS Lambda automatically monitors Lambda functions on your behalf and sends logs to Amazon CloudWatch. Your Lambda function comes with a CloudWatch Logs log group and a log stream for each instance of your function. The Lambda runtime environment sends details about each invocation to the log stream, and relays logs and other output from your function's code. For more information, see [Sending Lambda function logs to CloudWatch Logs](monitoring-cloudwatchlogs.md).

This page describes how to produce log output from your Lambda function's code, and access logs using the AWS Command Line Interface, the Lambda console, or the CloudWatch console.

**Topics**
+ [Creating a function that returns logs](#node-logging-output)
+ [Using Lambda advanced logging controls with Node.js](#node-js-logging-advanced)
+ [Viewing logs in the Lambda console](#nodejs-logging-console)
+ [Viewing logs in the CloudWatch console](#nodejs-logging-cwconsole)
+ [Viewing logs using the AWS Command Line Interface (AWS CLI)](#nodejs-logging-cli)
+ [Deleting logs](#nodejs-logging-delete)

## Creating a function that returns logs
<a name="node-logging-output"></a>

To output logs from your function code, you can use methods on the [console object](https://developer.mozilla.org/en-US/docs/Web/API/Console), or any logging library that writes to `stdout` or `stderr`. The following example logs the values of environment variables and the event object.

**Note**  
We recommend that you use techniques such as input validation and output encoding when logging inputs. If you log input data directly, an attacker might be able to use your code to make tampering hard to detect, forge log entries, or bypass log monitors. For more information, see [Improper Output Neutralization for Logs](https://cwe.mitre.org/data/definitions/117.html) in the *Common Weakness Enumeration*. 

**Example index.js file – Logging**  

```
exports.handler = async function(event, context) {
  console.log("ENVIRONMENT VARIABLES\n" + JSON.stringify(process.env, null, 2))
  console.info("EVENT\n" + JSON.stringify(event, null, 2))
  console.warn("Event not processed.")
  return context.logStreamName
}
```

**Example log format**  

```
START RequestId: c793869b-ee49-115b-a5b6-4fd21e8dedac Version: $LATEST
2019-06-07T19:11:20.562Z	c793869b-ee49-115b-a5b6-4fd21e8dedac	INFO	ENVIRONMENT VARIABLES
{
  "AWS_LAMBDA_FUNCTION_VERSION": "$LATEST",
  "AWS_LAMBDA_LOG_GROUP_NAME": "/aws/lambda/my-function",
  "AWS_LAMBDA_LOG_STREAM_NAME": "2019/06/07/[$LATEST]e6f4a0c4241adcd70c262d34c0bbc85c",
  "AWS_EXECUTION_ENV": "AWS_Lambda_nodejs12.x",
  "AWS_LAMBDA_FUNCTION_NAME": "my-function",
  "PATH": "/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
  "NODE_PATH": "/opt/nodejs/node10/node_modules:/opt/nodejs/node_modules:/var/runtime/node_modules",
  ...
}
2019-06-07T19:11:20.563Z	c793869b-ee49-115b-a5b6-4fd21e8dedac	INFO	EVENT
{
  "key": "value"
}
2019-06-07T19:11:20.564Z	c793869b-ee49-115b-a5b6-4fd21e8dedac	WARN	Event not processed.
END RequestId: c793869b-ee49-115b-a5b6-4fd21e8dedac
REPORT RequestId: c793869b-ee49-115b-a5b6-4fd21e8dedac	Duration: 128.83 ms	Billed Duration: 296 ms	Memory Size: 128 MB	Max Memory Used: 74 MB	Init Duration: 166.62 ms	XRAY TraceId: 1-5d9d007f-0a8c7fd02xmpl480aed55ef0	SegmentId: 3d752xmpl1bbe37e	Sampled: true
```

The Node.js runtime logs the `START`, `END`, and `REPORT` lines for each invocation. It adds a timestamp, request ID, and log level to each entry logged by the function. The report line provides the following details.

**REPORT line data fields**
+ **RequestId** – The unique request ID for the invocation.
+ **Duration** – The amount of time that your function's handler method spent processing the event.
+ **Billed Duration** – The amount of time billed for the invocation.
+ **Memory Size** – The amount of memory allocated to the function.
+ **Max Memory Used** – The amount of memory used by the function. When invocations share an execution environment, Lambda reports the maximum memory used across all invocations. This behavior might result in a higher than expected reported value.
+ **Init Duration** – For the first request served, the amount of time it took the runtime to load the function and run code outside of the handler method.
+ **XRAY TraceId** – For traced requests, the [AWS X-Ray trace ID](services-xray.md).
+ **SegmentId** – For traced requests, the X-Ray segment ID.
+ **Sampled** – For traced requests, the sampling result.

You can view logs in the Lambda console, in the CloudWatch Logs console, or from the command line.

## Using Lambda advanced logging controls with Node.js
<a name="node-js-logging-advanced"></a>

To give you more control over how your functions’ logs are captured, processed, and consumed, you can configure the following logging options for supported Node.js runtimes:
+ **Log format** - select between plain text and structured JSON format for your function’s logs
+ **Log level** - for logs in JSON format, choose the detail level of the logs Lambda sends to Amazon CloudWatch, such as ERROR, DEBUG, or INFO
+ **Log group** - choose the CloudWatch log group your function sends logs to

For more information about these logging options, and instructions on how to configure your function to use them, see [Configuring advanced logging controls for Lambda functions](monitoring-logs.md#monitoring-cloudwatchlogs-advanced).

To use the log format and log level options with your Node.js Lambda functions, see the guidance in the following sections.

### Using structured JSON logs with Node.js
<a name="nodejs-logging-advanced-JSON"></a>

If you select JSON for your function’s log format, Lambda will send logs output using the console methods of `console.trace`, `console.debug`, `console.log`, `console.info`, `console.error`, and `console.warn` to CloudWatch as structured JSON. Each JSON log object contains at least four key value pairs with the following keys:
+ `"timestamp"` - the time the log message was generated
+ `"level"` - the log level assigned to the message
+ `"message"` - the contents of the log message
+ `"requestId"` - the unique request ID for the function invocation

Depending on the logging method that your function uses, this JSON object may also contain additional key pairs. For example, if your function uses `console` methods to log error objects using multiple arguments, the JSON object will contain extra key value pairs with the keys `errorMessage`, `errorType`, and `stackTrace`.

If your code already uses another logging library, such as Powertools for AWS Lambda, to produce JSON structured logs, you don’t need to make any changes. Lambda doesn’t double-encode any logs that are already JSON encoded, so your function’s application logs will continue to be captured as before.

For more information about using the Powertools for AWS Lambda logging package to create JSON structured logs in the Node.js runtime, see [Log and monitor TypeScript Lambda functions](typescript-logging.md).

#### Example JSON formatted log outputs
<a name="nodejs-logging-examples"></a>

The following examples shows how various log outputs generated using the `console` methods with single and multiple arguments are captured in CloudWatch Logs when you set your function's log format to JSON.

The first example uses the `console.error` method to output a simple string.

**Example Node.js logging code**  

```
export const handler = async (event) => {
  console.error("This is a warning message");
  ...
}
```

**Example JSON log record**  

```
{
    "timestamp":"2025-11-01T00:21:51.358Z",
    "level":"ERROR",
    "message":"This is a warning message",
    "requestId":"93f25699-2cbf-4976-8f94-336a0aa98c6f"
}
```

You can also output more complex structured log messages using either single or multiple arguments with the `console` methods. In the next example, you use `console.log` to output two key value pairs using a single argument. Note that the `"message"` field in the JSON object Lambda sends to CloudWatch Logs is not stringified.

**Example Node.js logging code**  

```
export const handler = async (event) => {
  console.log({data: 12.3, flag: false});
  ...
}
```

**Example JSON log record**  

```
{
    "timestamp": "2025-12-08T23:21:04.664Z",
    "level": "INFO",
    "requestId": "405a4537-9226-4216-ac59-64381ec8654a",
    "message": {
        "data": 12.3,
        "flag": false
    }
}
```

In the next example, you again use the `console.log` method to create a log output. This time, the method takes two arguments, a map containing two key value pairs and an identifying string. Note that in this case, because you have supplied two arguments, Lambda stringifies the `"message"` field.

**Example Node.js logging code**  

```
export const handler = async (event) => {
  console.log('Some object - ', {data: 12.3, flag: false});
  ...
}
```

**Example JSON log record**  

```
{
    "timestamp": "2025-12-08T23:21:04.664Z",
    "level": "INFO",
    "requestId": "405a4537-9226-4216-ac59-64381ec8654a",
    "message": "Some object -  { data: 12.3, flag: false }"
}
```

Lambda assigns outputs generated using `console.log` the log level INFO.

The final example shows how error objects can be output to CloudWatch Logs using the `console` methods. Note that when you log error objects using multiple arguments, Lambda adds the fields `errorMessage`, `errorType`, and `stackTrace` to the log output.

**Example Node.js logging code**  

```
export const handler = async (event) => {
  let e1 = new ReferenceError("some reference error");
  let e2 = new SyntaxError("some syntax error");
  console.log(e1);
  console.log("errors logged - ", e1, e2);
};
```

**Example JSON log record**  

```
{
    "timestamp": "2025-12-08T23:21:04.632Z",
    "level": "INFO",
    "requestId": "405a4537-9226-4216-ac59-64381ec8654a",
    "message": {
        "errorType": "ReferenceError",
        "errorMessage": "some reference error",
        "stackTrace": [
            "ReferenceError: some reference error",
            "    at Runtime.handler (file:///var/task/index.mjs:3:12)",
            "    at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1173:29)"
        ]
    }
}

{
    "timestamp": "2025-12-08T23:21:04.646Z",
    "level": "INFO",
    "requestId": "405a4537-9226-4216-ac59-64381ec8654a",
    "message": "errors logged -  ReferenceError: some reference error\n    at Runtime.handler (file:///var/task/index.mjs:3:12)\n    at Runtime.handleOnceNonStreaming 
    (file:///var/runtime/index.mjs:1173:29) SyntaxError: some syntax error\n    at Runtime.handler (file:///var/task/index.mjs:4:12)\n    at Runtime.handleOnceNonStreaming 
    (file:///var/runtime/index.mjs:1173:29)",
    "errorType": "ReferenceError",
    "errorMessage": "some reference error",
    "stackTrace": [
        "ReferenceError: some reference error",
        "    at Runtime.handler (file:///var/task/index.mjs:3:12)",
        "    at Runtime.handleOnceNonStreaming (file:///var/runtime/index.mjs:1173:29)"
    ]
}
```

When logging multiple error types, the extra fields `errorMessage`, `errorType`, and `stackTrace` are extracted from the first error type supplied to the `console` method.

### Using embedded metric format (EMF) client libraries with structured JSON logs
<a name="nodejs-logging-advanced-emf"></a>

AWS provides open-sourced client libraries for Node.js which you can use to create [embedded metric format](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Libraries.html) (EMF) logs. If you have existing functions that use these libraries and you change your function's log format to JSON, CloudWatch may no longer recognize the metrics emitted by your code.

If your code currently emits EMF logs directly using `console.log` or by using Powertools for AWS Lambda (TypeScript), CloudWatch will also be unable to parse these if you change your function's log format to JSON.

**Important**  
To ensure that your functions' EMF logs continue to be properly parsed by CloudWatch, update your [EMF](https://www.npmjs.com/package/aws-embedded-metrics) and [Powertools for AWS Lambda](https://github.com/aws-powertools/powertools-lambda-typescript) libraries to the latest versions. If switching to the JSON log format, we also recommend that you carry out testing to ensure compatibility with your function's embedded metrics. If your code emits EMF logs directly using `console.log`, change your code to output those metrics directly to `stdout` as shown in the following code example.

**Example code emitting embedded metrics to `stdout`**  

```
process.stdout.write(JSON.stringify(
    {
        "_aws": {
            "Timestamp": Date.now(),
            "CloudWatchMetrics": [{
                "Namespace": "lambda-function-metrics",
                "Dimensions": [["functionVersion"]],
                "Metrics": [{
                    "Name": "time",
                    "Unit": "Milliseconds",
                    "StorageResolution": 60
                }]
            }]
        },
        "functionVersion": "$LATEST",
        "time": 100,
        "requestId": context.awsRequestId
    }
) + "\n")
```

### Using log-level filtering with Node.js
<a name="nodejs-logging-advanced-level"></a>

For AWS Lambda to filter your application logs according to their log level, your function must use JSON formatted logs. You can achieve this in two ways:
+ Create log outputs using the standard console methods and configure your function to use JSON log formatting. AWS Lambda then filters your log outputs using the “level” key value pair in the JSON object described in [Using structured JSON logs with Node.js](#nodejs-logging-advanced-JSON). To learn how to configure your function’s log format, see [Configuring advanced logging controls for Lambda functions](monitoring-logs.md#monitoring-cloudwatchlogs-advanced).
+ Use another logging library or method to create JSON structured logs in your code that include a “level” key value pair defining the level of the log output. For example, you can use Powertools for AWS Lambda to generate JSON structured log outputs from your code. See [Log and monitor TypeScript Lambda functions](typescript-logging.md) to learn more about using Powertools with the Node.js runtime.

  For Lambda to filter your function's logs, you must also include a `"timestamp"` key value pair in your JSON log output. The time must be specified in valid [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) timestamp format. If you don't supply a valid timestamp, Lambda will assign the log the level INFO and add a timestamp for you.

When you configure your function to use log-level filtering, you select the level of logs you want AWS Lambda to send to CloudWatch Logs from the following options:


| Log level | Standard usage | 
| --- | --- | 
| TRACE (most detail) | The most fine-grained information used to trace the path of your code's execution | 
| DEBUG | Detailed information for system debugging | 
| INFO | Messages that record the normal operation of your function | 
| WARN | Messages about potential errors that may lead to unexpected behavior if unaddressed | 
| ERROR | Messages about problems that prevent the code from performing as expected | 
| FATAL (least detail) | Messages about serious errors that cause the application to stop functioning | 

Lambda sends logs of the selected level and lower to CloudWatch. For example, if you configure a log level of WARN, Lambda will send logs corresponding to the WARN, ERROR, and FATAL levels.

## Viewing logs in the Lambda console
<a name="nodejs-logging-console"></a>

You can use the Lambda console to view log output after you invoke a Lambda function.

If your code can be tested from the embedded **Code** editor, you will find logs in the **execution results**. When you use the console test feature to invoke a function, you'll find **Log output** in the **Details** section.

## Viewing logs in the CloudWatch console
<a name="nodejs-logging-cwconsole"></a>

You can use the Amazon CloudWatch console to view logs for all Lambda function invocations.

**To view logs on the CloudWatch console**

1. Open the [Log groups page](https://console.aws.amazon.com/cloudwatch/home?#logs:) on the CloudWatch console.

1. Choose the log group for your function (**/aws/lambda/*your-function-name***).

1. Choose a log stream.

Each log stream corresponds to an [instance of your function](lambda-runtime-environment.md). A log stream appears when you update your Lambda function, and when additional instances are created to handle concurrent invocations. To find logs for a specific invocation, we recommend instrumenting your function with AWS X-Ray. X-Ray records details about the request and the log stream in the trace.

## Viewing logs using the AWS Command Line Interface (AWS CLI)
<a name="nodejs-logging-cli"></a>

The AWS CLI is an open-source tool that enables you to interact with AWS services using commands in your command line shell. To complete the steps in this section, you must have the [AWS CLI version 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).

You can use the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) to retrieve logs for an invocation using the `--log-type` command option. The response contains a `LogResult` field that contains up to 4 KB of base64-encoded logs from the invocation.

**Example retrieve a log ID**  
The following example shows how to retrieve a *log ID* from the `LogResult` field for a function named `my-function`.  

```
aws lambda invoke --function-name my-function out --log-type Tail
```
You should see the following output:  

```
{
    "StatusCode": 200,
    "LogResult": "U1RBUlQgUmVxdWVzdElkOiA4N2QwNDRiOC1mMTU0LTExZTgtOGNkYS0yOTc0YzVlNGZiMjEgVmVyc2lvb...",
    "ExecutedVersion": "$LATEST"
}
```

**Example decode the logs**  
In the same command prompt, use the `base64` utility to decode the logs. The following example shows how to retrieve base64-encoded logs for `my-function`.  

```
aws lambda invoke --function-name my-function out --log-type Tail \
--query 'LogResult' --output text --cli-binary-format raw-in-base64-out | base64 --decode
```
The **cli-binary-format** option is required if you're using AWS CLI version 2. To make this the default setting, run `aws configure set cli-binary-format raw-in-base64-out`. For more information, see [AWS CLI supported global command line options](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list) in the *AWS Command Line Interface User Guide for Version 2*.  
You should see the following output:  

```
START RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8 Version: $LATEST
"AWS_SESSION_TOKEN": "AgoJb3JpZ2luX2VjELj...", "_X_AMZN_TRACE_ID": "Root=1-5d02e5ca-f5792818b6fe8368e5b51d50;Parent=191db58857df8395;Sampled=0"",ask/lib:/opt/lib",
END RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8
REPORT RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8  Duration: 79.67 ms      Billed Duration: 80 ms         Memory Size: 128 MB     Max Memory Used: 73 MB
```
The `base64` utility is available on Linux, macOS, and [Ubuntu on Windows](https://docs.microsoft.com/en-us/windows/wsl/install-win10). macOS users may need to use `base64 -D`.

**Example get-logs.sh script**  
In the same command prompt, use the following script to download the last five log events. The script uses `sed` to remove quotes from the output file, and sleeps for 15 seconds to allow time for the logs to become available. The output includes the response from Lambda and the output from the `get-log-events` command.   
Copy the contents of the following code sample and save in your Lambda project directory as `get-logs.sh`.  
The **cli-binary-format** option is required if you're using AWS CLI version 2. To make this the default setting, run `aws configure set cli-binary-format raw-in-base64-out`. For more information, see [AWS CLI supported global command line options](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list) in the *AWS Command Line Interface User Guide for Version 2*.  

```
#!/bin/bash
aws lambda invoke --function-name my-function --cli-binary-format raw-in-base64-out --payload '{"key": "value"}' out
sed -i'' -e 's/"//g' out
sleep 15
aws logs get-log-events --log-group-name /aws/lambda/my-function --log-stream-name stream1 --limit 5
```

**Example macOS and Linux (only)**  
In the same command prompt, macOS and Linux users may need to run the following command to ensure the script is executable.  

```
chmod -R 755 get-logs.sh
```

**Example retrieve the last five log events**  
In the same command prompt, run the following script to get the last five log events.  

```
./get-logs.sh
```
You should see the following output:  

```
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
{
    "events": [
        {
            "timestamp": 1559763003171,
            "message": "START RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf Version: $LATEST\n",
            "ingestionTime": 1559763003309
        },
        {
            "timestamp": 1559763003173,
            "message": "2019-06-05T19:30:03.173Z\t4ce9340a-b765-490f-ad8a-02ab3415e2bf\tINFO\tENVIRONMENT VARIABLES\r{\r  \"AWS_LAMBDA_FUNCTION_VERSION\": \"$LATEST\",\r ...",
            "ingestionTime": 1559763018353
        },
        {
            "timestamp": 1559763003173,
            "message": "2019-06-05T19:30:03.173Z\t4ce9340a-b765-490f-ad8a-02ab3415e2bf\tINFO\tEVENT\r{\r  \"key\": \"value\"\r}\n",
            "ingestionTime": 1559763018353
        },
        {
            "timestamp": 1559763003218,
            "message": "END RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf\n",
            "ingestionTime": 1559763018353
        },
        {
            "timestamp": 1559763003218,
            "message": "REPORT RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf\tDuration: 26.73 ms\tBilled Duration: 27 ms \tMemory Size: 128 MB\tMax Memory Used: 75 MB\t\n",
            "ingestionTime": 1559763018353
        }
    ],
    "nextForwardToken": "f/34783877304859518393868359594929986069206639495374241795",
    "nextBackwardToken": "b/34783877303811383369537420289090800615709599058929582080"
}
```

## Deleting logs
<a name="nodejs-logging-delete"></a>

Log groups aren't deleted automatically when you delete a function. To avoid storing logs indefinitely, delete the log group, or [configure a retention period](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html#SettingLogRetention) after which logs are deleted automatically.

# Instrumenting Node.js code in AWS Lambda
<a name="nodejs-tracing"></a>

Lambda integrates with AWS X-Ray to help you trace, debug, and optimize Lambda applications. You can use X-Ray to trace a request as it traverses resources in your application, which may include Lambda functions and other AWS services.

To send tracing data to X-Ray, you can use one of two SDK libraries:
+ [AWS Distro for OpenTelemetry (ADOT)](https://aws.amazon.com/otel) – A secure, production-ready, AWS-supported distribution of the OpenTelemetry (OTel) SDK.
+ [AWS X-Ray SDK for Node.js](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs.html) – An SDK for generating and sending trace data to X-Ray.

Each of the SDKs offer ways to send your telemetry data to the X-Ray service. You can then use X-Ray to view, filter, and gain insights into your application's performance metrics to identify issues and opportunities for optimization.

**Important**  
The X-Ray and Powertools for AWS Lambda SDKs are part of a tightly integrated instrumentation solution offered by AWS. The ADOT Lambda Layers are part of an industry-wide standard for tracing instrumentation that collect more data in general, but may not be suited for all use cases. You can implement end-to-end tracing in X-Ray using either solution. To learn more about choosing between them, see [Choosing between the AWS Distro for Open Telemetry and X-Ray SDKs](https://docs.aws.amazon.com/xray/latest/devguide/xray-instrumenting-your-app.html#xray-instrumenting-choosing).

**Topics**
+ [Using ADOT to instrument your Node.js functions](#nodejs-adot)
+ [Using the X-Ray SDK to instrument your Node.js functions](#nodejs-xray-sdk)
+ [Activating tracing with the Lambda console](#nodejs-tracing-console)
+ [Activating tracing with the Lambda API](#nodejs-tracing-api)
+ [Activating tracing with CloudFormation](#nodejs-tracing-cloudformation)
+ [Interpreting an X-Ray trace](#nodejs-tracing-interpretation)
+ [Storing runtime dependencies in a layer (X-Ray SDK)](#nodejs-tracing-layers)

## Using ADOT to instrument your Node.js functions
<a name="nodejs-adot"></a>

ADOT provides fully managed Lambda [layers](chapter-layers.md) that package everything you need to collect telemetry data using the OTel SDK. By consuming this layer, you can instrument your Lambda functions without having to modify any function code. You can also configure your layer to do custom initialization of OTel. For more information, see [Custom configuration for the ADOT Collector on Lambda](https://aws-otel.github.io/docs/getting-started/lambda#custom-configuration-for-the-adot-collector-on-lambda) in the ADOT documentation.

For Node.js runtimes, you can add the **AWS managed Lambda layer for ADOT Javascript** to automatically instrument your functions. For detailed instructions on how to add this layer, see [AWS Distro for OpenTelemetry Lambda Support for JavaScript](https://aws-otel.github.io/docs/getting-started/lambda/lambda-js) in the ADOT documentation.

## Using the X-Ray SDK to instrument your Node.js functions
<a name="nodejs-xray-sdk"></a>

To record details about calls that your Lambda function makes to other resources in your application, you can also use the AWS X-Ray SDK for Node.js. To get the SDK, add the `aws-xray-sdk-core` package to your application's dependencies.

**Example [blank-nodejs/package.json](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/blank-nodejs/package.json)**  

```
{
  "name": "blank-nodejs",
  "version": "1.0.0",
  "private": true,
  "devDependencies": {
    "jest": "29.7.0"
  },
  "dependencies": {
    "@aws-sdk/client-lambda": "3.345.0",
    "aws-xray-sdk-core": "3.5.3"
  },
  "scripts": {
    "test": "jest"
  }
}
```

To instrument AWS SDK clients in the [AWS SDK for JavaScript v3](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html), wrap the client instance with the `captureAWSv3Client` method.

**Example [blank-nodejs/function/index.js](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/blank-nodejs/function/index.js) – Tracing an AWS SDK client**  

```
const AWSXRay = require('aws-xray-sdk-core');
const { LambdaClient, GetAccountSettingsCommand } = require('@aws-sdk/client-lambda');

// Create client outside of handler to reuse
const lambda = AWSXRay.captureAWSv3Client(new LambdaClient());

// Handler
exports.handler = async function(event, context) {
    event.Records.forEach(record => {
  ...
```

The Lambda runtime sets some environment variables to configure the X-Ray SDK. For example, Lambda sets `AWS_XRAY_CONTEXT_MISSING` to `LOG_ERROR` to avoid throwing runtime errors from the X-Ray SDK. To set a custom context missing strategy, override the environment variable in your function configuration to have no value, and then you can set the context missing strategy programmatically.

**Example initialization code**  

```
const AWSXRay = require('aws-xray-sdk-core');

// Configure the context missing strategy to do nothing
AWSXRay.setContextMissingStrategy(() => {});
```

For more information, see [Working with Lambda environment variables](configuration-envvars.md).

After you add the correct dependencies and make the necessary code changes, activate tracing in your function's configuration via the Lambda console or the API.

## Activating tracing with the Lambda console
<a name="nodejs-tracing-console"></a>

To toggle active tracing on your Lambda function with the console, follow these steps:

**To turn on active tracing**

1. Open the [Functions page](https://console.aws.amazon.com/lambda/home#/functions) of the Lambda console.

1. Choose a function.

1. Choose **Configuration** and then choose **Monitoring and operations tools**.

1. Under **Additional monitoring tools**, choose **Edit**.

1. Under **CloudWatch Application Signals and AWS X-Ray**, choose **Enable** for **Lambda service traces**.

1. Choose **Save**.

## Activating tracing with the Lambda API
<a name="nodejs-tracing-api"></a>

Configure tracing on your Lambda function with the AWS CLI or AWS SDK, use the following API operations:
+ [UpdateFunctionConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateFunctionConfiguration.html)
+ [GetFunctionConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_GetFunctionConfiguration.html)
+ [CreateFunction](https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html)

The following example AWS CLI command enables active tracing on a function named **my-function**.

```
aws lambda update-function-configuration --function-name my-function \
--tracing-config Mode=Active
```

Tracing mode is part of the version-specific configuration when you publish a version of your function. You can't change the tracing mode on a published version.

## Activating tracing with CloudFormation
<a name="nodejs-tracing-cloudformation"></a>

To activate tracing on an `AWS::Lambda::Function` resource in an CloudFormation template, use the `TracingConfig` property.

**Example [function-inline.yml](https://github.com/awsdocs/aws-lambda-developer-guide/blob/master/templates/function-inline.yml) – Tracing configuration**  

```
Resources:
  function:
    Type: [AWS::Lambda::Function](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html)
    Properties:
      TracingConfig:
        Mode: Active
      ...
```

For an AWS Serverless Application Model (AWS SAM) `AWS::Serverless::Function` resource, use the `Tracing` property.

**Example [template.yml](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/blank-nodejs/template.yml) – Tracing configuration**  

```
Resources:
  function:
    Type: [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html)
    Properties:
      Tracing: Active
      ...
```

## Interpreting an X-Ray trace
<a name="nodejs-tracing-interpretation"></a>

Your function needs permission to upload trace data to X-Ray. When you activate tracing in the Lambda console, Lambda adds the required permissions to your function's [execution role](lambda-intro-execution-role.md). Otherwise, add the [AWSXRayDaemonWriteAccess](https://console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess) policy to the execution role.

After you've configured active tracing, you can observe specific requests through your application. The [ X-Ray service graph](https://docs.aws.amazon.com/xray/latest/devguide/aws-xray.html#xray-concepts-servicegraph) shows information about your application and all its components. The following example shows an application with two functions. The primary function processes events and sometimes returns errors. The second function at the top processes errors that appear in the first's log group and uses the AWS SDK to call X-Ray, Amazon Simple Storage Service (Amazon S3), and Amazon CloudWatch Logs.

![\[\]](http://docs.aws.amazon.com/lambda/latest/dg/images/sample-errorprocessor-servicemap.png)


X-Ray doesn't trace all requests to your application. X-Ray applies a sampling algorithm to ensure that tracing is efficient, while still providing a representative sample of all requests. The sampling rate is 1 request per second and 5 percent of additional requests. You can't configure the X-Ray sampling rate for your functions.

In X-Ray, a *trace* records information about a request that is processed by one or more *services*. Lambda records 2 segments per trace, which creates two nodes on the service graph. The following image highlights these two nodes:

![\[\]](http://docs.aws.amazon.com/lambda/latest/dg/images/xray-servicemap-function.png)


The first node on the left represents the Lambda service, which receives the invocation request. The second node represents your specific Lambda function. The following example shows a trace with these two segments. Both are named **my-function**, but one has an origin of `AWS::Lambda` and the other has an origin of `AWS::Lambda::Function`. If the `AWS::Lambda` segment shows an error, the Lambda service had an issue. If the `AWS::Lambda::Function` segment shows an error, your function had an issue.

![\[\]](http://docs.aws.amazon.com/lambda/latest/dg/images/V2_sandbox_images/my-function-2-v1.png)


This example expands the `AWS::Lambda::Function` segment to show its three subsegments.

**Note**  
AWS is currently implementing changes to the Lambda service. Due to these changes, you may see minor differences between the structure and content of system log messages and trace segments emitted by different Lambda functions in your AWS account.  
The example trace shown here illustrates the old-style function segment. The differences between the old- and new-style segments are described in the following paragraphs.  
These changes will be implemented during the coming weeks, and all functions in all AWS Regions except the China and GovCloud regions will transition to use the new-format log messages and trace segments.

The old-style function segment contains the following subsegments:
+ **Initialization** – Represents time spent loading your function and running [initialization code](foundation-progmodel.md). This subsegment only appears for the first event that each instance of your function processes.
+ **Invocation** – Represents the time spent running your handler code.
+ **Overhead** – Represents the time the Lambda runtime spends preparing to handle the next event.

The new-style function segment doesn't contain an `Invocation` subsegment. Instead, customer subsegments are attached directly to the function segment. For more information about the structure of the old- and new-style function segments, see [Understanding X-Ray traces](services-xray.md#services-xray-traces).

You can also instrument HTTP clients, record SQL queries, and create custom subsegments with annotations and metadata. For more information, see the [AWS X-Ray SDK for Node.js](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-nodejs.html) in the *AWS X-Ray Developer Guide*.

**Pricing**  
You can use X-Ray tracing for free each month up to a certain limit as part of the AWS Free Tier. Beyond that threshold, X-Ray charges for trace storage and retrieval. For more information, see [AWS X-Ray pricing](https://aws.amazon.com/xray/pricing/).

## Storing runtime dependencies in a layer (X-Ray SDK)
<a name="nodejs-tracing-layers"></a>

If you use the X-Ray SDK to instrument AWS SDK clients your function code, your deployment package can become quite large. To avoid uploading runtime dependencies every time you update your function code, package the X-Ray SDK in a [Lambda layer](chapter-layers.md).

The following example shows an `AWS::Serverless::LayerVersion` resource that stores the AWS X-Ray SDK for Node.js.

**Example [template.yml](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/blank-nodejs/template.yml) – Dependencies layer**  

```
Resources:
  function:
    Type: [AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html)
    Properties:
      CodeUri: function/.
      Tracing: Active
      Layers:
        - !Ref libs
      ...
  libs:
    Type: [AWS::Serverless::LayerVersion](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-layerversion.html)
    Properties:
      LayerName: blank-nodejs-lib
      Description: Dependencies for the blank sample app.
      ContentUri: lib/.
      CompatibleRuntimes:
        - nodejs24.x
```

With this configuration, you update the library layer only if you change your runtime dependencies. Since the function deployment package contains only your code, this can help reduce upload times.

Creating a layer for dependencies requires build changes to generate the layer archive prior to deployment. For a working example, see the [blank-nodejs](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/blank-nodejs) sample application.