Leverage these proven patterns and best practices to build production-ready functions that are secure, performant, and maintainable.
Code Quality
Follow consistent naming conventions
Implement proper error handling
Use TypeScript for better type safety
Keep functions focused and small
Security & Performance
Validate all input data
Use environment variables for secrets
Implement proper authentication
Optimize for cold start performance
Request Function Patterns
HTTP Method Handling
REST API Handler
Example Request
Success Response
export const handler = async ( event ) => {
const { httpMethod , body } = event ;
const data = body ? JSON . parse ( body ) : {};
switch ( httpMethod ) {
case 'GET' :
return {
statusCode: 200 ,
body: JSON . stringify ({ users: await getUsers () })
};
case 'POST' :
const newUser = await createUser ( data );
return {
statusCode: 201 ,
body: JSON . stringify ({ id: newUser . id , message: 'User created' })
};
case 'PUT' :
const updatedUser = await updateUser ( data );
return {
statusCode: 200 ,
body: JSON . stringify ( updatedUser )
};
case 'DELETE' :
await deleteUser ( data . id );
return {
statusCode: 204 ,
body: ''
};
default :
return {
statusCode: 405 ,
body: JSON . stringify ({ error: 'Method not allowed' })
};
}
};
Validation Logic
Invalid Input
Validation Error Response
export const handler = async ( event ) => {
const { body } = event ;
try {
const data = JSON . parse ( body || '{}' );
// Validate required fields
const errors = [];
if ( ! data . email ) errors . push ( 'Email is required' );
if ( ! data . password ) errors . push ( 'Password is required' );
if ( data . password && data . password . length < 8 ) {
errors . push ( 'Password must be at least 8 characters' );
}
// Return validation errors
if ( errors . length > 0 ) {
return {
statusCode: 400 ,
body: JSON . stringify ({
error: 'Validation failed' ,
details: errors
})
};
}
// Process valid data
const result = await processUser ( data );
return {
statusCode: 200 ,
body: JSON . stringify ( result )
};
} catch ( error ) {
return {
statusCode: 400 ,
body: JSON . stringify ({ error: 'Invalid JSON' })
};
}
};
Error Handling
Error Handler
Error Types
Error Response Format
export const handler = async ( event ) => {
try {
const result = await processRequest ( event );
return {
statusCode: 200 ,
body: JSON . stringify ({ success: true , data: result })
};
} catch ( error ) {
console . error ( 'Request processing error:' , error );
// Handle validation errors
if ( error . name === 'ValidationError' ) {
return {
statusCode: 400 ,
body: JSON . stringify ({
error: 'Invalid input' ,
details: error . message
})
};
}
// Handle not found errors
if ( error . name === 'NotFoundError' ) {
return {
statusCode: 404 ,
body: JSON . stringify ({ error: 'Resource not found' })
};
}
// Handle unexpected errors
return {
statusCode: 500 ,
body: JSON . stringify ({
error: 'Internal server error' ,
requestId: event . requestContext ?. requestId
})
};
}
};
Security Best Practices
Authentication & Authorization
JWT Validation
Input Sanitization
import jwt from 'jsonwebtoken' ;
export const handler = async ( event ) => {
try {
// Extract token from header
const authHeader = event . headers . Authorization || event . headers . authorization ;
if ( ! authHeader ?. startsWith ( 'Bearer ' )) {
return {
statusCode: 401 ,
body: JSON . stringify ({ error: 'Missing or invalid authorization header' })
};
}
const token = authHeader . substring ( 7 );
const decoded = jwt . verify ( token , process . env . JWT_SECRET );
// Check user permissions
if ( ! decoded . permissions ?. includes ( 'admin' )) {
return {
statusCode: 403 ,
body: JSON . stringify ({ error: 'Insufficient permissions' })
};
}
// Continue with authenticated request
const result = await processAuthenticatedRequest ( decoded , event );
return {
statusCode: 200 ,
body: JSON . stringify ( result )
};
} catch ( error ) {
return {
statusCode: 401 ,
body: JSON . stringify ({ error: 'Invalid token' })
};
}
};
Environment Variables & Secrets
Secure Configuration
Rate Limiting
// Use environment variables for sensitive data
const config = {
database: {
host: process . env . DB_HOST ,
username: process . env . DB_USERNAME ,
password: process . env . DB_PASSWORD ,
ssl: process . env . NODE_ENV === 'production'
},
jwt: {
secret: process . env . JWT_SECRET ,
expiresIn: process . env . JWT_EXPIRES_IN || '24h'
},
external: {
apiKey: process . env . EXTERNAL_API_KEY ,
webhookSecret: process . env . WEBHOOK_SECRET
}
};
// Validate required environment variables
const requiredEnvVars = [ 'DB_HOST' , 'JWT_SECRET' , 'EXTERNAL_API_KEY' ];
const missingVars = requiredEnvVars . filter ( varName => ! process . env [ varName ]);
if ( missingVars . length > 0 ) {
throw new Error ( `Missing required environment variables: ${ missingVars . join ( ', ' ) } ` );
}
Cold Start Optimization
Module Initialization
Response Caching
// Initialize outside handler for reuse across invocations
import { createConnection } from './database' ;
import { initializeCache } from './cache' ;
let dbConnection ;
let cache ;
// Initialize on first import
const initializeResources = async () => {
if ( ! dbConnection ) {
dbConnection = await createConnection ();
}
if ( ! cache ) {
cache = await initializeCache ();
}
};
export const handler = async ( event ) => {
// Ensure resources are initialized
await initializeResources ();
// Use cached resources
const result = await dbConnection . query ( 'SELECT * FROM users' );
await cache . set ( 'users' , result , 300 ); // Cache for 5 minutes
return {
statusCode: 200 ,
body: JSON . stringify ( result )
};
};
Database Optimization
Connection Pooling
Batch Operations
import { Pool } from 'pg' ;
// Create connection pool outside handler
const pool = new Pool ({
host: process . env . DB_HOST ,
database: process . env . DB_NAME ,
user: process . env . DB_USER ,
password: process . env . DB_PASSWORD ,
max: 10 , // Maximum pool size
idleTimeoutMillis: 30000 ,
connectionTimeoutMillis: 2000 ,
});
export const handler = async ( event ) => {
const client = await pool . connect ();
try {
// Use parameterized queries to prevent SQL injection
const result = await client . query (
'SELECT * FROM users WHERE email = $1 AND status = $2' ,
[ event . body . email , 'active' ]
);
return {
statusCode: 200 ,
body: JSON . stringify ( result . rows )
};
} finally {
client . release (); // Always release the client back to pool
}
};
Testing Best Practices
Unit Testing
Function Testing
Mock External Services
import { handler } from '../src/user-handler' ;
describe ( 'User Handler' , () => {
test ( 'should create user with valid data' , async () => {
// Arrange
const event = {
httpMethod: 'POST' ,
body: JSON . stringify ({
name: 'John Doe' ,
email: 'john@example.com' ,
password: 'securePassword123'
})
};
// Act
const response = await handler ( event );
const body = JSON . parse ( response . body );
// Assert
expect ( response . statusCode ). toBe ( 201 );
expect ( body ). toHaveProperty ( 'id' );
expect ( body . message ). toBe ( 'User created' );
});
test ( 'should return validation error for invalid email' , async () => {
// Arrange
const event = {
httpMethod: 'POST' ,
body: JSON . stringify ({
name: 'John Doe' ,
email: 'invalid-email' ,
password: 'securePassword123'
})
};
// Act
const response = await handler ( event );
const body = JSON . parse ( response . body );
// Assert
expect ( response . statusCode ). toBe ( 400 );
expect ( body . error ). toBe ( 'Validation failed' );
expect ( body . details ). toContain ( 'Valid email is required' );
});
});
Response Function Patterns
Conditional Enhancement
Conditional Logic
Error Enhancement
Performance Optimization
export const handler = async ( event ) => {
const { request , response } = event ;
const responseBody = JSON . parse ( response . body );
// Only enhance specific endpoints
if ( request . path . includes ( '/api/' )) {
// Add API-specific enhancements
return {
... response ,
headers: {
... response . headers ,
'X-API-Version' : '1.0' ,
'X-Rate-Limit' : '1000'
},
body: JSON . stringify ({
... responseBody ,
meta: {
version: '1.0' ,
timestamp: new Date (). toISOString ()
}
})
};
}
return response ; // Return unchanged for other endpoints
};
Task Function Patterns
Error Handling and Retries
Retry Logic
Batch Processing
Progress Tracking
export const handler = async ( event ) => {
const maxRetries = 3 ;
let attempt = 0 ;
while ( attempt < maxRetries ) {
try {
const result = await performTask ();
return {
statusCode: 200 ,
body: JSON . stringify ({
success: true ,
attempt: attempt + 1 ,
result
})
};
} catch ( error ) {
attempt ++ ;
console . error ( `Task attempt ${ attempt } failed:` , error );
if ( attempt >= maxRetries ) {
// Send notification on final failure
await sendTaskFailureNotification ( error );
return {
statusCode: 500 ,
body: JSON . stringify ({
error: 'Task failed after maximum retries' ,
attempts: attempt ,
lastError: error . message
})
};
}
// Wait before retrying (exponential backoff)
await new Promise ( resolve => setTimeout ( resolve , Math . pow ( 2 , attempt ) * 1000 ));
}
}
};
Cron Expressions
Rate Expressions
Schedule Examples
// Every day at 2:30 AM
cron ( 30 2 * * ? * )
// Every weekday at 9:00 AM
cron ( 0 9 ? * MON - FRI * )
// Every 15 minutes
cron ( */ 15 * * * ? * )
// First day of every month at midnight
cron ( 0 0 1 * ? * )
// Every Sunday at 6:00 PM
cron ( 0 18 ? * SUN * )
General Development Tips
Function Design
Keep functions small and focused
Use TypeScript for better developer experience
Follow single responsibility principle
Return consistent response formats
Error Handling
Always use try-catch blocks
Return appropriate HTTP status codes
Log errors for debugging
Provide helpful error messages
Performance
Initialize resources outside the handler
Use connection pooling for databases
Implement caching where appropriate
Batch operations when possible
Security
Validate and sanitize all inputs
Use environment variables for secrets
Implement authentication and authorization
Add rate limiting to prevent abuse
Response Function Guidelines
Fail Gracefully Always return the original response if your enhancement fails. Don’t break the user experience.
Keep It Fast Response functions should be lightweight. Heavy processing can slow down responses.
Be Selective Not every response needs enhancement. Use conditional logic to enhance only when needed.
Log Errors Log enhancement errors for debugging, but don’t let them affect the user’s response.
Task Function Guidelines
Keep Tasks Idempotent Design tasks to handle being run multiple times safely. Check for existing work before processing.
Process in Batches For large datasets, process items in small batches to avoid timeouts and memory issues.
Monitor and Alert Set up monitoring for task failures and send notifications when critical tasks fail.
Handle Failures Gracefully Implement retry logic with exponential backoff. Store failed items for manual review.
Next Steps
Pro Tip: Start with simple patterns and gradually add complexity. Focus on getting the core functionality working before optimizing for performance.