Create AWS Lambda Functions from JavaScript Callbacks

The aws:lambda/callbackFunction:CallbackFunction resource, part of the Pulumi AWS provider, packages JavaScript functions into Lambda deployments automatically, handling code serialization, dependency bundling, and AWS configuration. This guide focuses on three capabilities: inline JavaScript deployment, factory functions for cold start optimization, and API Gateway event handling.

CallbackFunction creates IAM roles with broad default policies when you don’t specify role or policies explicitly. The examples may reference npm packages that must be in your project dependencies. The examples are intentionally small. Combine them with your own IAM configuration, VPC networking, 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 file 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 and returns values directly.

Optimize cold starts with factory functions

Applications with expensive setup (Express servers, database connections) benefit from running initialization once at module load time 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 module. It performs expensive initialization (creating the Express app, configuring middleware), then returns the actual handler function that Lambda invokes for each request. This separates one-time setup from per-request logic, reducing cold start impact on subsequent invocations.

Handle API Gateway requests with typed events

API Gateway integrations require functions that accept specific event shapes and return HTTP-formatted responses.

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 and Context parameters with typed access to request details. The function returns an object with statusCode and body properties that API Gateway translates into HTTP responses. The RestAPI resource wires the function to API Gateway routes automatically.

Beyond these examples

These snippets focus on specific CallbackFunction features: inline JavaScript code packaging, factory functions for initialization optimization, and API Gateway event handling. They’re intentionally minimal rather than full serverless applications.

The examples may reference pre-existing infrastructure such as npm packages (express, aws-serverless-express, aws-apigateway). They focus on function configuration rather than provisioning IAM roles or VPC networking.

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

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

These omissions are intentional: the goal is to illustrate how CallbackFunction packages and deploys JavaScript code, not provide drop-in serverless 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 Definition & Handlers
What's the difference between callback and callbackFactory?
Use callback for simple handlers that run on every invocation. Use callbackFactory when you have expensive initialization work (like setting up an Express server) that should run once when the Lambda module loads. You must provide exactly one, not both.
Should I use async functions for my Lambda handler?
Yes, async functions are recommended. Without async, Lambda execution continues until the callback parameter is called AND the event loop is empty, which can cause unexpected delays.
Permissions & IAM
What are the default IAM permissions for CallbackFunction?
If you don’t specify role or policies, CallbackFunction creates a role with FullAccess to many services: Lambda, CloudWatch, S3, DynamoDB, SQS, Kinesis, CloudFormation, Cognito, and X-Ray. For production, explicitly provide role or policies following least-privilege principles.
Can I provide both role and policies?
No, role and policies are mutually exclusive. Provide role to use an existing IAM role ARN, or policies to have CallbackFunction create a role with specific policy ARNs attached.
Code Packaging & Layers
How do I customize which files are included in my function bundle?
Use codePathOptions.extraIncludePaths to add local directories or files, and codePathOptions.extraExcludePackages to remove unneeded Node.js packages from the bundle.
How do I share code between multiple Lambda functions using layers?
Create an aws.lambda.LayerVersion with your shared code packaged in an AssetArchive, then attach it to functions using the layers property. Layer contents are extracted to /opt at runtime.
Why isn't my Lambda finding the Node.js module from my layer?
Node.js dependencies must be placed at nodejs/node_modules path within the layer zip file. Also add path mapping to your tsconfig.json with baseUrl and paths configuration to resolve imports during compilation.
Configuration & Limits
What are the timeout limits for CallbackFunction?
The default timeout is 3 seconds, with a maximum of 900 seconds (15 minutes). Valid range is 1 to 900 seconds.
What are the memory limits for CallbackFunction?
Memory defaults to 128 MB and can be configured between 128 MB and 10,240 MB (10 GB) in 1 MB increments using the memorySize property.
Is durableConfig available in all regions?
No, durableConfig may only be available in limited regions, including us-east-2.

Using a different cloud?

Explore serverless guides for other cloud providers: