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

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' })
      };
  }
};

Input Validation

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

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

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

// 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(', ')}`);
}

Performance Optimization

Cold Start Optimization

// 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

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

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

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

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));
    }
  }
};

Schedule Formats

// 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.