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 Effect handlers vs async handlers for plain handler patterns, or the Lambda guide for the full Effect-based approach with bindings, event sources, and sinks.
Async Functions
Section titled “Async Functions”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
import * as AWS from "alchemy/AWS";
const func = yield* AWS.Lambda.Function("ApiFunction", { main: "./src/handler.ts", url: true,});Function using ARM64
const func = yield* AWS.Lambda.Function("ArmFunction", { main: "./src/handler.ts", architecture: "arm64",});Function with a native package (Sharp)
const func = yield* AWS.Lambda.Function("ImageProcessor", { main: "./src/handler.ts", architecture: "arm64", build: { install: ["sharp"], },});Writing the async handler
export const handler = async (event: any) => { return { statusCode: 200, body: JSON.stringify({ message: "Hello from Lambda!" }), };};Effect Functions
Section titled “Effect Functions”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.url, url: true }, Effect.gen(function* () { // init: bind resources const getItem = yield* AWS.DynamoDB.GetItem(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); }), }; }),) {}Configuration
Section titled “Configuration”Function with URL
const func = yield* AWS.Lambda.Function("ApiFunction", { main: "./src/handler.ts", url: true,});Function URL with IAM auth
const func = yield* AWS.Lambda.Function("ApiFunction", { main: "./src/handler.ts", url: { authType: "AWS_IAM", },});Function in a VPC
const func = yield* AWS.Lambda.Function("VpcFunction", { main: "./src/handler.ts", vpc: { subnetIds: ["subnet-abc123", "subnet-def456"], securityGroupIds: ["sg-xyz789"], },});Async invocation retries and failure destination
const func = yield* AWS.Lambda.Function("AsyncFunction", { main: "./src/handler.ts", eventInvokeConfig: { maximumRetryAttempts: 0, maximumEventAge: "1 minute", destinationConfig: { OnFailure: { Destination: queue.queueArn, }, }, },});Bundling & Tree-shaking
Section titled “Bundling & Tree-shaking”main is bundled with rolldown at deploy time. Top-level calls in the
effect, @effect/*, alchemy, @alchemy.run/*, and
@distilled.cloud/* packages receive #__PURE__ annotations by
default, so anything the function doesn’t use from those packages is
tree-shaken out of the bundle. Any other package — including your own
app — is left untouched unless you list it explicitly.
Treat additional packages as pure
Pass package names (or picomatch globs) via build.pure.packages to
annotate them in addition to the defaults. Listing a package that also
declares "sideEffects": false (or []) in its package.json opts it
into full annotation — top-level calls whose result is discarded are
deleted under minification when unused — so only list packages whose
modules really are free of meaningful top-level side effects.
const func = yield* AWS.Lambda.Function("ApiFunction", { main: "./src/handler.ts", build: { pure: { packages: ["my-lib", "@my-scope/*"] }, },});Disable pure annotations
const func = yield* AWS.Lambda.Function("ApiFunction", { main: "./src/handler.ts", build: { pure: false },});EFS File Systems
Section titled “EFS File Systems”Mount an EFS access point into the function’s /mnt/… file system. The
function must be attached to a VPC that can reach an EFS mount target for
the file system.
Mount an EFS access point via props
const accessPoint = yield* AWS.EFS.AccessPoint("FilesAccess", { fileSystemId: fileSystem.fileSystemId, posixUser: { uid: 1000, gid: 1000 },});
const func = yield* AWS.Lambda.Function("FilesFunction", { main: "./src/handler.ts", vpc: { subnetIds, securityGroupIds }, fileSystemConfigs: [ // pass the AccessPoint resource itself (or its ARN via `arn`) { accessPoint, localMountPath: "/mnt/files" }, ],});Mount via the host-agnostic EFS.mount binding
EFS.mount wires the same mount config plus least-privilege IAM through
the binding channel and works on both Lambda and ECS hosts.
export default class FilesFunction extends AWS.Lambda.Function<FilesFunction>()( "FilesFunction", { main: import.meta.url, vpc: { subnetIds, securityGroupIds } }, Effect.gen(function* () { const files = yield* AWS.EFS.mount(accessPoint, { path: "/mnt/files" }); return Effect.fn(function* (event: unknown) { return { mountedAt: files.path }; }); }).pipe(Effect.provide(AWS.EFS.MountLive)),) {}S3 Bindings
Section titled “S3 Bindings”Bind S3 operations in the init phase to give the function IAM permissions and inject the bucket name as an environment variable.
// initconst getObject = yield* S3.GetObject(bucket);const putObject = yield* S3.PutObject(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"); }),};DynamoDB Bindings
Section titled “DynamoDB Bindings”Bind DynamoDB operations in the init phase to grant table-scoped IAM permissions.
// initconst getItem = yield* AWS.DynamoDB.GetItem(table);const putItem = yield* AWS.DynamoDB.PutItem(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); }),};SQS Bindings
Section titled “SQS Bindings”Bind SQS operations in the init phase to send messages to a queue.
// initconst sendMessage = yield* SQS.SendMessage(queue);
return { fetch: Effect.gen(function* () { // runtime yield* sendMessage({ MessageBody: JSON.stringify({ orderId: "123" }), }); return HttpServerResponse.text("Queued"); }),};SNS Bindings
Section titled “SNS Bindings”Bind SNS operations in the init phase to publish messages to a topic.
// initconst publish = yield* AWS.SNS.Publish(topic);
return { fetch: Effect.gen(function* () { // runtime yield* publish({ Message: JSON.stringify({ event: "order.created" }), Subject: "OrderCreated", }); return HttpServerResponse.text("Published"); }),};Kinesis Bindings
Section titled “Kinesis Bindings”Bind Kinesis operations in the init phase to put records into a stream.
// initconst putRecord = yield* AWS.Kinesis.PutRecord(stream);
return { fetch: Effect.gen(function* () { // runtime yield* putRecord({ PartitionKey: "order-123", Data: new TextEncoder().encode(JSON.stringify({ orderId: "123" })), }); return HttpServerResponse.text("Sent"); }),};Event Sources
Section titled “Event Sources”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.consumeQueueMessages(queue, Effect.fn(function* (message) { yield* Effect.log(`Received: ${message.body}`); }),);Process DynamoDB stream changes
yield* AWS.DynamoDB.consumeTableChanges(table, { StreamViewType: "NEW_AND_OLD_IMAGES",}, Effect.fn(function* (record) { yield* Effect.log(`Change: ${record.eventName}`); }),);Process S3 notifications
yield* AWS.S3.consumeBucketEvents(bucket, { events: ["s3:ObjectCreated:*"],}, (stream) => stream.pipe( Stream.runForEach((event) => Effect.log(`New object: ${event.key}`), ), ),);