Skip to content

Function

Source: src/AWS/Lambda/Function.ts

An AWS Lambda host resource that combines code bundling, IAM role provisioning, and runtime binding collection.

Function is the canonical runtime host for AWS. Alchemy automatically bundles your TypeScript entry module with Rolldown, creates an IAM execution role, and uploads the zip artifact. On subsequent deploys, the function is only updated when the bundle hash changes.

There are two ways to define a Lambda Function:

  • Async — plain handler export, no Effect runtime in the bundle.
  • Effect — Effect implementation with typed bindings and event sources.

See the {@link https://alchemy.run/guides/async-lambda | Async Lambda Guide} for plain handler patterns, or the {@link https://alchemy.run/guides/lambda | Effect Lambda Guide} for the full Effect-based approach with bindings, event sources, and sinks.

Point main at a file that exports a standard Lambda handler. No Effect runtime is included in the bundle. Useful when migrating existing Lambda functions or when you don’t need Effect.

Defining an async Lambda in your stack

alchemy.run.ts
import * as AWS from "alchemy/AWS";
const func = yield* AWS.Lambda.Function("ApiFunction", {
main: "./src/handler.ts",
url: true,
});

Writing the async handler

src/handler.ts
export const handler = async (event: any) => {
return {
statusCode: 200,
body: JSON.stringify({ message: "Hello from Lambda!" }),
};
};

Pass the Effect implementation as the third argument. Bindings attach IAM permissions and environment variables at deploy time, while the runtime execution context collects listeners and exports.

export default class ApiFunction extends AWS.Lambda.Function<ApiFunction>()(
"ApiFunction",
{ main: import.meta.filename, url: true },
Effect.gen(function* () {
// init: bind resources
const getItem = yield* DynamoDB.GetItem.bind(table);
return {
// runtime: use them
fetch: Effect.gen(function* () {
const request = yield* HttpServerRequest;
const url = new URL(request.url);
const id = url.searchParams.get("id");
const result = yield* getItem({ Key: { pk: { S: id! } } });
return yield* HttpServerResponse.json(result.Item);
}),
};
}),
) {}

Function with URL

const func = yield* AWS.Lambda.Function("ApiFunction", {
main: "./src/handler.ts",
url: true,
});

Function in a VPC

const func = yield* AWS.Lambda.Function("VpcFunction", {
main: "./src/handler.ts",
vpc: {
subnetIds: ["subnet-abc123", "subnet-def456"],
securityGroupIds: ["sg-xyz789"],
},
});

Bind S3 operations in the init phase to give the function IAM permissions and inject the bucket name as an environment variable.

// init
const getObject = yield* S3.GetObject.bind(bucket);
const putObject = yield* S3.PutObject.bind(bucket);
return {
fetch: Effect.gen(function* () {
// runtime
yield* putObject({ Key: "hello.txt", Body: "Hello!" });
const obj = yield* getObject({ Key: "hello.txt" });
return HttpServerResponse.text("OK");
}),
};

Bind DynamoDB operations in the init phase to grant table-scoped IAM permissions.

// init
const getItem = yield* DynamoDB.GetItem.bind(table);
const putItem = yield* DynamoDB.PutItem.bind(table);
return {
fetch: Effect.gen(function* () {
// runtime
yield* putItem({ Item: { pk: { S: "user#1" }, name: { S: "Alice" } } });
const result = yield* getItem({ Key: { pk: { S: "user#1" } } });
return yield* HttpServerResponse.json(result.Item);
}),
};

Bind SQS operations in the init phase to send messages to a queue.

// init
const sendMessage = yield* SQS.SendMessage.bind(queue);
return {
fetch: Effect.gen(function* () {
// runtime
yield* sendMessage({
MessageBody: JSON.stringify({ orderId: "123" }),
});
return HttpServerResponse.text("Queued");
}),
};

Bind SNS operations in the init phase to publish messages to a topic.

// init
const publish = yield* SNS.Publish.bind(topic);
return {
fetch: Effect.gen(function* () {
// runtime
yield* publish({
Message: JSON.stringify({ event: "order.created" }),
Subject: "OrderCreated",
});
return HttpServerResponse.text("Published");
}),
};

Bind Kinesis operations in the init phase to put records into a stream.

// init
const putRecord = yield* Kinesis.PutRecord.bind(stream);
return {
fetch: Effect.gen(function* () {
// runtime
yield* putRecord({
PartitionKey: "order-123",
Data: new TextEncoder().encode(JSON.stringify({ orderId: "123" })),
});
return HttpServerResponse.text("Sent");
}),
};

Lambda functions can be triggered by event sources like SQS queues, DynamoDB streams, S3 notifications, SNS topics, and Kinesis streams.

Process SQS messages

yield* SQS.messages(queue).process(
Effect.fn(function* (message) {
yield* Effect.log(`Received: ${message.body}`);
}),
);

Process DynamoDB stream changes

yield* DynamoDB.streams(table, {
StreamViewType: "NEW_AND_OLD_IMAGES",
}).process(
Effect.fn(function* (record) {
yield* Effect.log(`Change: ${record.eventName}`);
}),
);

Process S3 notifications

yield* S3.notifications(bucket, {
events: ["s3:ObjectCreated:*"],
}).subscribe((stream) =>
stream.pipe(
Stream.runForEach((event) =>
Effect.log(`New object: ${event.key}`),
),
),
);