Define Lambda function handler in Node.js - AWS Lambda

Define Lambda function handler in Node.js

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.

Node.js handler basics

The following example function logs the contents of the event object and returns the location of the logs.

Note

This page shows examples of both CommonJS and ES module handlers. To learn about the difference between these two handler types, see Designating a function handler as an ES module.

ES module handler
export const handler = async (event, context) => { console.log("EVENT: \n" + JSON.stringify(event, null, 2)); return context.logStreamName; };
CommonJS module handler
exports.handler = async function (event, context) { console.log("EVENT: \n" + JSON.stringify(event, null, 2)); return context.logStreamName; };

When you configure a function, the value of the handler setting is the file name and the name of the exported handler method, separated by a dot. The default in the console and for examples in this guide is index.handler. This indicates the handler method that's exported from the index.js file.

The runtime passes arguments to the handler method. The first argument is the event object, which contains information from the invoker. The invoker passes this information as a JSON-formatted string when it calls Invoke, and the runtime converts it to an object. When an AWS service invokes your function, the event structure varies by service.

The second argument is the context object, which contains information about the invocation, function, and execution environment. In the preceding example, the function gets the name of the log stream from the context object and returns it to the invoker.

You can also use a callback argument, which is a function that you can call in non-async handlers to send a response. We recommend that you use async/await instead of callbacks. Async/await provides improved readability, error handling, and efficiency. For more information about the differences between async/await and callbacks, see Using callbacks.

Naming

When you configure a function, the value of the 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 page of the Lambda console and choose your function.

  2. Choose the Code tab.

  3. Scroll down to the Runtime settings pane and choose Edit.

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

  5. Choose Save.

Using async/await

If your code performs an asynchronous task, use the async/await pattern to make sure that the handler finishes running. Async/await is a concise and readable way to write asynchronous code in Node.js, 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.

The async keyword marks a function as asynchronous, and the await keyword pauses the execution of the function until a Promise is resolved.

Note

Make sure to wait for asynchronous events to complete. If the function returns before async events are complete, the function might fail or cause unexpected behavior in your application. This can happen when a forEach loop contains an async event. forEach loops expect a synchronous call. For more information, see Array.prototype.forEach() in the Mozilla documentation.

ES module handler
Example – HTTP request with async/await
const url = "https://aws.amazon.com/"; export const handler = async(event) => { try { // fetch is available in Node.js 18 and later runtimes const res = await fetch(url); console.info("status", res.status); return res.status; } catch (e) { console.error(e); return 500; } };
CommonJS module handler
Example – HTTP request with async/await
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; };

The next example uses async/await to list your Amazon Simple Storage Service buckets.

Note

Before using this example, make sure that your function's execution role has Amazon S3 read permissions.

ES module handler
Example – AWS SDK v3 with async/await

This example uses the AWS SDK for JavaScript v3, which is available in nodejs18.x and later runtimes.

import {S3Client, ListBucketsCommand} from '@aws-sdk/client-s3'; const s3 = new S3Client({region: 'us-east-1'}); export const handler = async(event) => { const data = await s3.send(new ListBucketsCommand({})); return data.Buckets; };
CommonJS module handler
Example – AWS SDK v3 with async/await

This example uses the AWS SDK for JavaScript v3, which is available in nodejs18.x and later runtimes.

const { S3Client, ListBucketsCommand } = require('@aws-sdk/client-s3'); const s3 = new S3Client({ region: 'us-east-1' }); exports.handler = async (event) => { const data = await s3.send(new ListBucketsCommand({})); return data.Buckets; };

Using callbacks

We recommend that you use async/await to declare the function handler instead of using callbacks. Async/await is a better choice for several reasons:

  • Readability: Async/await code is easier to read and understand than callback code, which can quickly become difficult to follow and result in callback hell.

  • Debugging and error handling: Debugging callback-based code can be difficult. The call stack can become hard to follow and errors can easily be swallowed. With async/await, you can use try/catch blocks to handle errors.

  • Efficiency: Callbacks often require switching between different parts of the code. Async/await can reduce the number of context switches, resulting in more efficient code.

When you use callbacks in your handler, the function continues to execute until the event loop 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 to false.

The callback function takes two arguments: an Error and a response. The response object must be compatible with JSON.stringify.

The following example function checks a URL and returns the status code to the invoker.

ES module handler
Example – HTTP request with callback
import https from "https"; let url = "https://aws.amazon.com/"; export function handler(event, context, callback) { https.get(url, (res) => { callback(null, res.statusCode); }).on("error", (e) => { callback(Error(e)); }); }
CommonJS module handler
Example – HTTP request with callback
const https = require("https"); let url = "https://aws.amazon.com/"; exports.handler = function (event, context, callback) { https.get(url, (res) => { callback(null, res.statusCode); }).on("error", (e) => { callback(Error(e)); }); };

In the next example, the response from Amazon S3 is returned to the invoker as soon as it's available. The timeout running on the event loop is frozen, and it continues running the next time the function is invoked.

Note

Before using this example, make sure that your function's execution role has Amazon S3 read permissions.

ES module handler
Example – AWS SDK v3 with callbackWaitsForEmptyEventLoop

This example uses the AWS SDK for JavaScript v3, which is available in nodejs18.x and later runtimes.

import AWS from "@aws-sdk/client-s3"; const s3 = new AWS.S3({}); export const handler = function (event, context, callback) { context.callbackWaitsForEmptyEventLoop = false; s3.listBuckets({}, callback); setTimeout(function () { console.log("Timeout complete."); }, 5000); };
CommonJS module handler
Example – AWS SDK v3 with callbackWaitsForEmptyEventLoop

This example uses the AWS SDK for JavaScript v3, which is available in nodejs18.x and later runtimes.

const AWS = require("@aws-sdk/client-s3"); const s3 = new AWS.S3({}); exports.handler = function (event, context, callback) { context.callbackWaitsForEmptyEventLoop = false; s3.listBuckets({}, callback); setTimeout(function () { console.log("Timeout complete."); }, 5000); };

Code best practices for Node.js Lambda functions

Adhere to the guidelines in the following list to use best coding practices when building your Lambda functions:

  • Separate the Lambda handler from your core logic. This allows you to make a more unit-testable function. In Node.js this may look like:

    exports.myHandler = function(event, context, callback) { var foo = event.foo; var bar = event.bar; var result = MyLambdaFunction (foo, bar); callback(null, result); } function MyLambdaFunction (foo, bar) { // MyLambdaFunction logic here }
  • 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 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.

  • Use environment variables 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 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?.