Overview
This example demonstrates how to deploy BlueLibs Runner applications to AWS Lambda, showing two different deployment patterns:- Lambdalith: Single Lambda function handling all routes
- Per-Route: Each endpoint has its own dedicated Lambda function
Architecture
The example is structured for serverless deployments:Key Characteristics
- Cached Runner: Runner instance is reused across warm Lambda invocations
- Shutdown Hooks Disabled: Lambda manages process lifecycle
- Request Context: Request-scoped data via
createContext - Shared HTTP Utilities: Avoid duplication across handlers
Key Code Snippets
Shared Bootstrap
Pattern 1: Lambdalith (Single Handler)
handler.lambdalith.ts
Pattern 2: Per-Route Handlers
How to Run
Deployment Patterns Compared
Lambdalith (Single Handler)
Pros:- Simpler deployment (one function)
- Better cold start reuse (shared warm instances)
- Easier to share dependencies and state
- Lower AWS Lambda function count
- Larger function size
- All routes scale together
- Harder to set per-route configs (memory, timeout)
- Small to medium APIs
- Tightly coupled routes
- Cost-sensitive workloads (fewer functions)
Per-Route Handlers
Pros:- Fine-grained scaling (hot routes scale independently)
- Smaller function sizes (faster cold starts)
- Per-route configuration (memory, timeout, permissions)
- Better fault isolation
- More complex deployment
- Higher function count
- Potential code duplication without shared utilities
- Large APIs with varied traffic patterns
- Routes with different resource requirements
- Microservices architecture
Lambda Configuration
Shared Setup
Runner Caching
The runner is cached across warm invocations:- Resources are initialized once per container
- Warm starts are fast (no re-initialization)
- Cold starts pay initialization cost only once
What to Learn
1. Serverless Adaptability
Runner works seamlessly in serverless environments by:- Disabling shutdown hooks (Lambda manages lifecycle)
- Caching the runner instance across invocations
- Using request context for request-scoped data
2. Shared Business Logic
Tasks and resources are defined once inbootstrap.ts and reused across both deployment patterns. This keeps your business logic DRY and framework-agnostic.
3. Request Context
RequestCtx.provide() wraps each handler invocation, providing request-scoped data without global state:
4. Deployment Flexibility
Choose your pattern based on your needs:- Start with lambdalith for simplicity
- Split hot routes into per-route handlers as traffic grows
- Mix patterns (critical routes separate, others in lambdalith)