AWS Lambda TypeScript Integration
Patterns for creating high-performance AWS Lambda functions in TypeScript with optimized cold starts.
Overview
This skill provides complete patterns for AWS Lambda TypeScript development, covering two main approaches:
- NestJS Framework - Full-featured framework with dependency injection, modular architecture, and extensive ecosystem
- Raw TypeScript - Minimal overhead approach with maximum control and smaller bundle size
Both approaches support API Gateway and ALB integration with production-ready configurations.
When to Use
Use this skill when:
- Creating new Lambda functions in TypeScript
- Migrating existing TypeScript applications to Lambda
- Optimizing cold start performance for TypeScript Lambda
- Choosing between framework-based and minimal TypeScript approaches
- Configuring API Gateway or ALB integration
- Setting up deployment pipelines for TypeScript Lambda
Instructions
1. Choose Your Approach
| Approach | Cold Start | Bundle Size | Best For | Complexity |
|---|---|---|---|---|
| NestJS | < 500ms | Larger (100KB+) | Complex APIs, enterprise apps, DI needed | Medium |
| Raw TypeScript | < 100ms | Smaller (< 50KB) | Simple handlers, microservices, minimal deps | Low |
2. Project Structure
NestJS Structure
my-nestjs-lambda/
βββ src/
β βββ app.module.ts
β βββ main.ts
β βββ lambda.ts # Lambda entry point
β βββ modules/
β βββ api/
βββ package.json
βββ tsconfig.json
βββ serverless.yml
Raw TypeScript Structure
my-ts-lambda/
βββ src/
β βββ handlers/
β β βββ api.handler.ts
β βββ services/
β βββ utils/
βββ dist/ # Compiled output
βββ package.json
βββ tsconfig.json
βββ template.yaml
3. Implementation Examples
See the References section for detailed implementation guides. Quick examples:
NestJS Handler:
// lambda.ts import { NestFactory } from '@nestjs/core'; import { ExpressAdapter } from '@nestjs/platform-express'; import serverlessExpress from '@codegenie/serverless-express'; import { Context, Handler } from 'aws-lambda'; import express from 'express'; import { AppModule } from './src/app.module'; let cachedServer: Handler; async function bootstrap(): Promise<Handler> { const expressApp = express(); const adapter = new ExpressAdapter(expressApp); const nestApp = await NestFactory.create(AppModule, adapter); await nestApp.init(); return serverlessExpress({ app: expressApp }); } export const handler: Handler = async (event: any, context: Context) => { if (!cachedServer) { cachedServer = await bootstrap(); } return cachedServer(event, context); };
Raw TypeScript Handler:
// src/handlers/api.handler.ts import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; export const handler = async ( event: APIGatewayProxyEvent, context: Context ): Promise<APIGatewayProxyResult> => { return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: 'Hello from TypeScript Lambda!' }) }; };
Core Concepts
Cold Start Optimization
TypeScript cold start depends on bundle size and initialization code. Key strategies:
- Lazy Loading - Defer heavy imports until needed
- Tree Shaking - Remove unused code from bundle
- Minification - Use esbuild or terser for smaller bundles
- Instance Caching - Cache initialized services between invocations
See Raw TypeScript Lambda for detailed patterns.
Connection Management
Create clients at module level and reuse:
// GOOD: Initialize once, reuse across invocations import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; const dynamoClient = new DynamoDBClient({ region: process.env.AWS_REGION }); export const handler = async (event: APIGatewayProxyEvent) => { // Use dynamoClient - already initialized };
Environment Configuration
// src/config/env.config.ts export const env = { region: process.env.AWS_REGION || 'us-east-1', tableName: process.env.TABLE_NAME || '', debug: process.env.DEBUG === 'true', }; // Validate required variables if (!env.tableName) { throw new Error('TABLE_NAME environment variable is required'); }
Best Practices
Memory and Timeout Configuration
- Memory: Start with 512MB for NestJS, 256MB for raw TypeScript
- Timeout: Set based on cold start + expected processing time
- NestJS: 10-30 seconds for cold start buffer
- Raw TypeScript: 3-10 seconds typically sufficient
Dependencies
Keep package.json minimal:
{ "dependencies": { "aws-lambda": "^3.1.0", "@aws-sdk/client-dynamodb": "^3.450.0" }, "devDependencies": { "typescript": "^5.3.0", "esbuild": "^0.19.0" } }
Error Handling
Return proper HTTP codes with structured errors:
export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => { try { const result = await processEvent(event); return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(result) }; } catch (error) { console.error('Error processing request:', error); return { statusCode: 500, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ error: 'Internal server error' }) }; } };
Logging
Use structured logging for CloudWatch Insights:
const log = (level: string, message: string, meta?: object) => { console.log(JSON.stringify({ level, message, timestamp: new Date().toISOString(), ...meta })); }; log('info', 'Request processed', { requestId: context.awsRequestId });
Deployment Options
Quick Start
Serverless Framework:
service: my-typescript-api provider: name: aws runtime: nodejs20.x functions: api: handler: dist/handler.handler events: - http: path: /{proxy+} method: ANY
AWS SAM:
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Resources: ApiFunction: Type: AWS::Serverless::Function Properties: CodeUri: dist/ Handler: handler.handler Runtime: nodejs20.x Events: ApiEvent: Type: Api Properties: Path: /{proxy+} Method: ANY
For complete deployment configurations including CI/CD, see Serverless Deployment.
Constraints and Warnings
Lambda Limits
- Deployment package: 250MB unzipped maximum (50MB zipped)
- Memory: 128MB to 10GB
- Timeout: 15 minutes maximum
- Concurrent executions: 1000 default (adjustable)
- Environment variables: 4KB total size
TypeScript-Specific Considerations
- Bundle size: TypeScript compiles to JavaScript; use bundlers to minimize size
- Cold start: Node.js 20.x offers best performance
- Dependencies: Use Lambda Layers for shared dependencies
- Native modules: Must be compiled for Amazon Linux 2
Common Pitfalls
- Importing heavy libraries at module level - Defer to lazy loading if not always needed
- Not bundling dependencies - Include all production dependencies in the package
- Missing type definitions - Install
@types/aws-lambdafor proper event typing - No timeout handling - Use
context.getRemainingTimeInMillis()for long operations
Security Considerations
- Never hardcode credentials; use IAM roles and environment variables
- Validate all input data
- Use least privilege IAM policies
- Enable CloudTrail for audit logging
- Sanitize logs to avoid leaking sensitive data
References
For detailed guidance on specific topics:
- NestJS Lambda - Complete NestJS setup, dependency injection, Express/Fastify adapters
- Raw TypeScript Lambda - Minimal handler patterns, bundling, tree shaking
- Serverless Config - Serverless Framework and SAM configuration
- Serverless Deployment - CI/CD pipelines, environment management
- Testing - Jest, integration testing, SAM Local
Examples
Example 1: Create a NestJS REST API
Input:
Create a TypeScript Lambda REST API using NestJS for a todo application
Process:
- Initialize NestJS project with
nest new - Install Lambda dependencies:
@codegenie/serverless-express,aws-lambda - Create
lambda.tsentry point with Express adapter - Configure
serverless.ymlwith API Gateway events - Deploy with Serverless Framework
Output:
- Complete NestJS project structure
- REST API with CRUD endpoints
- DynamoDB integration
- Deployment configuration
Example 2: Create a Raw TypeScript Lambda
Input:
Create a minimal TypeScript Lambda function with optimal cold start
Process:
- Set up TypeScript project with esbuild
- Create handler with proper AWS types
- Configure minimal dependencies
- Set up SAM or Serverless deployment
- Optimize bundle size with tree shaking
Output:
- Minimal TypeScript Lambda project
- Optimized bundle < 50KB
- Cold start < 100ms
Example 3: Deploy with GitHub Actions
Input:
Configure CI/CD for TypeScript Lambda with SAM
Process:
- Create GitHub Actions workflow
- Set up Node.js environment
- Run tests with Jest
- Bundle with esbuild
- Deploy with SAM
Output:
- Complete
.github/workflows/deploy.yml - Multi-stage pipeline
- Integrated test automation
Version
Version: 1.0.0