Create AWS Lambda Functions from JavaScript Callbacks

The aws:lambda/callbackFunction:CallbackFunction resource, part of the Pulumi AWS provider, creates Lambda functions directly from JavaScript code, automatically packaging dependencies and managing deployment. This guide focuses on three capabilities: inline callback functions, factory functions for initialization, and API Gateway integration.

CallbackFunction automatically creates IAM roles with broad managed policies when you don’t specify role or policies. The examples assume internet access and may require additional npm packages. The examples are intentionally small. Combine them with your own IAM roles, VPC configuration, and event sources.

Create a function from inline JavaScript code

Most serverless deployments start with a simple function that responds to events, without manual ZIP creation or build steps.

import * as aws from "@pulumi/aws";

// Create an AWS Lambda function that fetches the Pulumi website and returns the HTTP status
const lambda = new aws.lambda.CallbackFunction("fetcher", {
    callback: async(event) => {
        try {
            const res = await fetch("https://www.pulumi.com/robots.txt");
            console.info("status", res.status);
            return res.status;
        }
        catch (e) {
            console.error(e);
            return 500;
        }
    },
});

The callback property accepts an async JavaScript function that becomes your Lambda handler. Pulumi serializes the function, packages dependencies, and uploads everything to AWS Lambda automatically. The function can use standard Node.js APIs like fetch without additional configuration.

Optimize cold starts with callbackFactory

Applications with expensive setup benefit from running initialization once per container rather than on every invocation.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as express from "express";
import * as serverlessExpress from "aws-serverless-express";
import * as middleware from "aws-serverless-express/middleware";

const lambda = new aws.lambda.CallbackFunction<any, any>("mylambda", {
  callbackFactory: () => {
    const app = express();
    app.use(middleware.eventContext());
    let ctx;

    app.get("/", (req, res) => {
      console.log("Invoked url: " + req.url);

      fetch('https://www.pulumi.com/robots.txt').then(resp => {
        res.json({
          message: "Hello, world!\n\nSucceeded with " + ctx.getRemainingTimeInMillis() + "ms remaining.",
          fetchStatus: resp.status,
          fetched: resp.text(),
        });
      });
    });

    const server = serverlessExpress.createServer(app);
    return (event, context) => {
      console.log("Lambda invoked");
      console.log("Invoked function: " + context.invokedFunctionArn);
      console.log("Proxying to express");
      ctx = context;
      serverlessExpress.proxy(server, event, <any>context);
    }
  }
});

The callbackFactory property accepts a function that runs once when Lambda loads your code. It performs expensive setup (creating Express apps, opening database connections) and returns the actual handler function. The context parameter provides Lambda runtime information like remaining execution time. This pattern reduces cold start impact for frameworks that need initialization.

Handle API Gateway requests with typed events

REST APIs route HTTP requests to Lambda, passing request details as structured event objects.

import * as apigateway from "@pulumi/aws-apigateway";
import { APIGatewayProxyEvent, Context } from "aws-lambda";

const api = new apigateway.RestAPI("api", {
    routes: [
        {
            path: "/api",
            eventHandler: async (event: APIGatewayProxyEvent, context: Context) => {
                return {
                    statusCode: 200,
                    body: JSON.stringify({
                        eventPath: event.path,
                        functionName: context.functionName,
                    })
                };
            },
        },
    ],
});

export const url = api.url;

The eventHandler receives APIGatewayProxyEvent objects containing request path, headers, and body. Your function returns an object with statusCode and body properties that API Gateway converts into HTTP responses. The RestAPI resource automatically wires the Lambda function as the backend for the specified route.

Beyond these examples

These snippets focus on specific CallbackFunction features: inline JavaScript callbacks and factory functions, automatic code packaging and dependency bundling, and API Gateway integration with typed events. They’re intentionally minimal rather than full serverless applications.

The examples don’t require pre-existing infrastructure but do rely on automatic IAM role creation with broad managed policies. They focus on function definition rather than security hardening or production configuration.

To keep things focused, common Lambda patterns are omitted, including:

  • IAM role and policy customization (role, policies properties)
  • Memory and timeout tuning (memorySize, timeout)
  • VPC networking (vpcConfig)
  • Environment variables and secrets (environment)
  • Lambda Layers for shared code (layers property)
  • Code bundle customization (codePathOptions)

These omissions are intentional: the goal is to illustrate how CallbackFunction serializes and deploys JavaScript code, not provide drop-in production modules. See the CallbackFunction resource reference for all available configuration options.

Let's create AWS Lambda Functions from JavaScript Callbacks

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Function Handlers & Initialization
What's the difference between callback and callbackFactory?
callback is the Lambda handler function itself, while callbackFactory is a function that returns the handler. Use callbackFactory for expensive initialization work that should run once when the Lambda module loads, rather than on every invocation.
Why should I use async functions for Lambda handlers?
Async functions ensure proper execution flow. Non-async functions will run until the callback parameter is called and the event loop is empty, which can cause unexpected behavior.
IAM Permissions & Security
What IAM permissions does CallbackFunction grant by default?

If neither role nor policies is specified, CallbackFunction creates an IAM role with these managed policies:

  • AWSLambda_FullAccess
  • CloudWatchFullAccessV2
  • CloudWatchEventsFullAccess
  • AmazonS3FullAccess
  • AmazonDynamoDBFullAccess
  • AmazonSQSFullAccess
  • AmazonKinesisFullAccess
  • AWSCloudFormationReadOnlyAccess
  • AmazonCognitoPowerUser
  • AWSXrayWriteOnlyAccess
Can I specify both role and policies?
No, only one of role or policies can be provided. If neither is provided, default managed policies are used instead.
Code Bundling & Lambda Layers
How do I customize which files are included in my function bundle?
Use codePathOptions with extraIncludePaths to add local files or folders, and extraExcludePackages to remove unneeded Node.js modules from the bundle.
What path do Lambda layers extract to at runtime?
Lambda layers extract to /opt. For Node.js dependencies, package them under nodejs/node_modules in the layer zip file to make them available on the Node.js module path.
How many Lambda layers can I attach to a function?
You can attach a maximum of 5 Lambda Layer Version ARNs to a Lambda function using the layers property.
Resource Limits & Configuration
What are the timeout limits for CallbackFunction?
Lambda functions default to 3 seconds and support a maximum of 900 seconds (15 minutes). Valid values are between 1 and 900.
What are the memory limits for Lambda functions?
Memory defaults to 128 MB and can be configured between 128 MB and 32,768 MB (32 GB) in 1 MB increments.
How does reservedConcurrentExecutions work?
This controls concurrent execution limits. A value of 0 disables the Lambda from being triggered, -1 removes any concurrency limitations (the default), and positive values reserve that many concurrent executions.
Immutability & Lifecycle
Which properties can't be changed after creation?
The following properties are immutable: name, packageType, and tenancyConfig. Changing these requires replacing the function.

Using a different cloud?

Explore serverless guides for other cloud providers: