AWS Lambda Example
This example demonstrates deploying a serverless application to AWS using Lambda Functions, DynamoDB Tables, SQS Queues, and IAM Roles.Features
- Lambda Function: Serverless compute with Function URL
- DynamoDB Table: NoSQL database with partition key
- SQS Queue: Message queue for async processing
- IAM Role: Execution role with appropriate permissions
- ESBuild Bundle: Optimized TypeScript bundling
Project Setup
import alchemy from "alchemy";
import { Function, Queue, Role, Table } from "alchemy/aws";
import { Bundle } from "alchemy/esbuild";
import path from "node:path";
import { fileURLToPath } from "node:url";
const app = await alchemy("aws-app");
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const [_queue, table, role] = await Promise.all([
Queue("queue", {
queueName: `${app.name}-${app.stage}-queue`,
visibilityTimeout: 30,
messageRetentionPeriod: 345600, // 4 days
}),
// Create DynamoDB table
Table("table", {
tableName: `${app.name}-${app.stage}-table`,
partitionKey: {
name: "id",
type: "S",
},
}),
// Create Lambda execution role with DynamoDB access
Role("role", {
roleName: `${app.name}-${app.stage}-lambda-role`,
assumeRolePolicy: {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: {
Service: "lambda.amazonaws.com",
},
Action: "sts:AssumeRole",
},
],
},
}),
]);
// Create Lambda function
const bundle = await Bundle("api-bundle", {
entryPoint: path.join(__dirname, "src", "index.ts"),
outdir: ".out",
format: "esm",
platform: "node",
target: "node20",
minify: true,
sourcemap: true,
// Don't bundle aws-sdk as it's provided by Lambda
external: ["@aws-sdk/*"],
});
const _api = await Function("api", {
functionName: `${app.name}-${app.stage}-api`,
bundle,
roleArn: role.arn,
handler: "index.handler",
environment: {
TABLE_NAME: table.tableName,
},
url: {
// Enable public access
authType: "NONE",
cors: {
allowOrigins: ["*"],
allowMethods: ["GET", "POST", "PUT", "DELETE"],
allowHeaders: ["content-type"],
},
},
});
await app.finalize();
export function handler(_event: any, _context: any) {
console.log("Hello, World!");
return {
statusCode: 200,
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message: "Hello from Lambda!",
timestamp: new Date().toISOString(),
}),
};
}
import { DynamoDBClient, PutItemCommand, GetItemCommand } from "@aws-sdk/client-dynamodb";
import type { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
const dynamodb = new DynamoDBClient({});
const TABLE_NAME = process.env.TABLE_NAME!;
export async function handler(
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> {
const method = event.requestContext.http.method;
if (method === "POST") {
// Create item
const body = JSON.parse(event.body || "{}");
await dynamodb.send(
new PutItemCommand({
TableName: TABLE_NAME,
Item: {
id: { S: crypto.randomUUID() },
data: { S: JSON.stringify(body) },
createdAt: { N: Date.now().toString() },
},
})
);
return {
statusCode: 201,
body: JSON.stringify({ message: "Created" }),
};
}
if (method === "GET") {
// Get item
const id = event.queryStringParameters?.id;
if (!id) {
return {
statusCode: 400,
body: JSON.stringify({ error: "Missing id parameter" }),
};
}
const result = await dynamodb.send(
new GetItemCommand({
TableName: TABLE_NAME,
Key: { id: { S: id } },
})
);
return {
statusCode: 200,
body: JSON.stringify(result.Item || {}),
};
}
return {
statusCode: 405,
body: JSON.stringify({ error: "Method not allowed" }),
};
}