Deploy to AWS Lambda
MetaScript's C backend creates tiny, fast binaries perfect for serverless. Get cold starts under 50ms.
Why MetaScript for Lambda?
| Metric | Node.js | Python | MetaScript (C) |
|---|---|---|---|
| Cold Start | 200-500ms | 300-800ms | <50ms |
| Binary Size | ~50MB | ~60MB | <2MB |
| Memory | 128MB min | 128MB min | 64MB works |
| Cost | $$ | $$ | $ |
Quick Start
Create Lambda Project
npm create metascript@latest my-lambda -- --template lambda
cd my-lambdaProject Structure
my-lambda/
├── src/
│ ├── handler.ms # Lambda handlers
│ └── lib/ # Shared code
├── build.ms # Build config
├── template.yaml # SAM template
└── package.jsonWriting Handlers
Basic Handler
import { lambda, APIGatewayEvent, Context } from "@metascript/lambda"
export const hello = lambda.handler(
async (event: APIGatewayEvent, context: Context) => {
return {
statusCode: 200,
body: JSON.stringify({
message: "Hello from MetaScript!",
requestId: context.awsRequestId,
}),
}
}
)With Request Parsing
import { lambda, APIGatewayEvent } from "@metascript/lambda"
@derive(Serialize, Deserialize)
class CreateUserRequest {
name: string
email: string
}
@derive(Serialize)
class CreateUserResponse {
id: string
name: string
}
export const createUser = lambda.handler(
async (event: APIGatewayEvent) => {
// Type-safe body parsing
const body = lambda.parseBody<CreateUserRequest>(event)
const user = await db.users.create({
name: body.name,
email: body.email,
})
return lambda.json<CreateUserResponse>({
id: user.id,
name: user.name,
})
}
)Error Handling
import { lambda, HttpError } from "@metascript/lambda"
export const getUser = lambda.handler(async (event) => {
const id = event.pathParameters?.id
if (!id) {
throw new HttpError(400, "Missing user ID")
}
const user = await db.users.find(id)
if (!user) {
throw new HttpError(404, "User not found")
}
return lambda.json(user)
})Configuration
SAM Template
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 10
MemorySize: 128
Runtime: provided.al2
Architectures:
- arm64
Resources:
HelloFunction:
Type: AWS::Serverless::Function
Properties:
Handler: bootstrap
CodeUri: dist/
Events:
Api:
Type: Api
Properties:
Path: /hello
Method: getBuild Configuration
// build.ms
export const package = {
name: "my-lambda",
version: "1.0.0",
backends: ["c"],
};
export const deps = {};
export const deps_c = {
// Native dependencies if needed
};Building and Deploying
Local Development
# Build and start local API
msc lambda dev
# Or use SAM
sam local start-apiBuild for Production
# Build optimized binary
msc build --target=c --release
# Package for Lambda
msc lambda packageDeploy
# Deploy with SAM
sam deploy --guided
# Or direct to Lambda
msc lambda deploy --function my-functionAdvanced Patterns
Middleware
import { lambda, Middleware } from "@metascript/lambda"
const auth: Middleware = async (event, context, next) => {
const token = event.headers.Authorization
if (!token) {
return { statusCode: 401, body: "Unauthorized" }
}
// Add user to context
context.user = await validateToken(token)
return next()
}
const logging: Middleware = async (event, context, next) => {
const start = Date.now()
const result = await next()
console.log(`${event.path} took ${Date.now() - start}ms`)
return result
}
export const handler = lambda
.use(logging)
.use(auth)
.handler(async (event, context) => {
return lambda.json({ user: context.user })
})Database Connections
import { lambda } from "@metascript/lambda"
import { db } from "./db"
// Connection reused across warm invocations
let connection: Connection | null = null
const getConnection = async () => {
if (!connection) {
connection = await db.connect(process.env.DATABASE_URL)
}
return connection
}
export const handler = lambda.handler(async (event) => {
const conn = await getConnection()
const users = await conn.query("SELECT * FROM users")
return lambda.json(users)
})SQS Trigger
import { lambda, SQSEvent } from "@metascript/lambda"
export const processQueue = lambda.handler(
async (event: SQSEvent) => {
for (const record of event.Records) {
const body = JSON.parse(record.body)
await processMessage(body)
}
return { batchItemFailures: [] }
}
)Performance Tips
1. Use ARM64
ARM64 (Graviton2) is ~34% cheaper and often faster:
Architectures:
- arm642. Minimize Dependencies
# Check binary size
ls -lh dist/bootstrap
# Should be <2MB for most functions3. Use Provisioned Concurrency
For latency-critical paths:
ProvisionedConcurrencyConfig:
ProvisionedConcurrentExecutions: 54. Enable SnapStart
SnapStart:
ApplyOn: PublishedVersionsNext Steps
- Build a CLI Tool - Create command-line tools
- Create OTP Service - Build with Erlang backend
- Memory Model - Understand ORC for Lambda