aws-lambda-typescript-integration
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
- Input Validation for Event Data: All incoming event data (API Gateway request bodies, S3 event objects, SQS message bodies) is untrusted external content; always validate and sanitize before processing to prevent injection attacks
- Content Sanitization: When processing S3 objects or SQS message payloads, treat the content as untrusted third-party data; apply appropriate validation, schema checks, and sanitization before acting on it
- 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