Pre-AlphaMetaScript is in early design phase. The compiler is not yet available.Join Discord for updates
MetaScript

Deploy to AWS Lambda

MetaScript's C backend creates tiny, fast binaries perfect for serverless. Get cold starts under 50ms.

Why MetaScript for Lambda?

MetricNode.jsPythonMetaScript (C)
Cold Start200-500ms300-800ms<50ms
Binary Size~50MB~60MB<2MB
Memory128MB min128MB min64MB works
Cost$$$$$

Quick Start

Create Lambda Project

npm create metascript@latest my-lambda -- --template lambda
cd my-lambda

Project Structure

my-lambda/
├── src/
│   ├── handler.ms       # Lambda handlers
│   └── lib/             # Shared code
├── build.ms             # Build config
├── template.yaml        # SAM template
└── package.json

Writing 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: get

Build 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-api

Build for Production

# Build optimized binary
msc build --target=c --release

# Package for Lambda
msc lambda package

Deploy

# Deploy with SAM
sam deploy --guided

# Or direct to Lambda
msc lambda deploy --function my-function

Advanced 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:
  - arm64

2. Minimize Dependencies

# Check binary size
ls -lh dist/bootstrap

# Should be <2MB for most functions

3. Use Provisioned Concurrency

For latency-critical paths:

ProvisionedConcurrencyConfig:
  ProvisionedConcurrentExecutions: 5

4. Enable SnapStart

SnapStart:
  ApplyOn: PublishedVersions

Next Steps