Serverless Functions in TypeScript: A Complete Guide to AWS Lambda
The serverless architecture has revolutionized the approach to backend development. Instead of managing servers, developers can focus solely on business logic. AWS Lambda is one of the most popular services for running serverless functions. In this guide, we will cover creating, configuring, and deploying Lambda functions in TypeScript — from basic concepts to production-ready solutions.
Why TypeScript for AWS Lambda?
TypeScript adds static typing to JavaScript, which is critically important for serverless functions:
- Type Safety — prevents errors at compile time
- Autocompletion — IDE suggests AWS SDK methods
- Readability — interfaces and types document data structures
- Scalability — easier to maintain code as the project grows
Project Setup
1. Initialization and Dependency Installation
Create a new project and install the necessary packages:
mkdir my-lambda-projectcd my-lambda-projectnpm init -y
npm install @types/aws-lambda aws-sdknpm install -D typescript ts-node @types/node2. TypeScript Configuration
Create a tsconfig.json file:
{ "compilerOptions": { "target": "ES2020", "module": "commonjs", "lib": ["ES2020"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "declaration": true, "declarationMap": true, "sourceMap": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"]}Creating Your First Lambda Function
Basic Handler Structure
Create a file src/handler.ts:
import { Handler, APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
interface UserData { userId: string; name: string; email: string;}
export const handler: Handler<APIGatewayProxyEvent, APIGatewayProxyResult> = async (event) => { try { // Parsing the request body const body: UserData = JSON.parse(event.body || '{}'); // Validation if (!body.userId || !body.name) { return { statusCode: 400, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, body: JSON.stringify({ error: 'Missing required fields' }) }; }
// Business logic const result = { id: body.userId, displayName: body.name.toUpperCase(), createdAt: new Date().toISOString() };
return { statusCode: 200, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, body: JSON.stringify(result) }; } catch (error) { console.error('Error processing request:', error); return { statusCode: 500, body: JSON.stringify({ error: 'Internal server error' }) }; }};Working with Environment and Secrets
Use environment variables for configuration:
import { Handler } from 'aws-lambda';
interface Config { DB_TABLE: string; MAX_RETRIES: number;}
export const handler: Handler = async (event) => { const config: Config = { DB_TABLE: process.env.DB_TABLE || 'default-table', MAX_RETRIES: parseInt(process.env.MAX_RETRIES || '3', 10) };
// Use AWS Secrets Manager or Parameter Store // for sensitive data return { statusCode: 200, body: JSON.stringify({ table: config.DB_TABLE }) };};