UNPKG

cdk-serverless-agentic-api

Version:

CDK construct for serverless web applications with CloudFront, S3, Cognito, API Gateway, and Lambda

186 lines (158 loc) 5.17 kB
/** * Health check Lambda function * Returns basic API status information with structured error handling and logging */ // Import structured logging utilities const { createLoggerFromEvent, MetricsCollector } = require('./logging'); // Standard CORS headers for responses const CORS_HEADERS = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Requested-With', 'Access-Control-Allow-Methods': 'GET,OPTIONS', 'Access-Control-Allow-Credentials': 'true', 'Access-Control-Max-Age': '86400' }; /** * Creates a standardized success response */ function createSuccessResponse(data, statusCode = 200, additionalHeaders = {}) { return { statusCode, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS, ...additionalHeaders }, body: JSON.stringify({ ...data, timestamp: new Date().toISOString() }) }; } /** * Creates a standardized error response */ function createErrorResponse(statusCode, error, message, requestId, details) { const errorResponse = { error, message, timestamp: new Date().toISOString() }; if (requestId) { errorResponse.requestId = requestId; } if (details && process.env.NODE_ENV === 'development') { errorResponse.details = details; } return { statusCode, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, body: JSON.stringify(errorResponse) }; } /** * Error handler wrapper for Lambda functions with structured logging */ function withErrorHandling(handler) { return async (event, context) => { const logger = createLoggerFromEvent(event, 'health-api'); const metrics = new MetricsCollector(logger); const startTime = Date.now(); try { logger.logRequestStart(event); const result = await handler(event, context, logger, metrics); const duration = Date.now() - startTime; const responseSize = result.body ? Buffer.byteLength(result.body, 'utf8') : 0; logger.logRequestEnd(result.statusCode, duration, responseSize); metrics.recordResponseTime('/api/health', event.httpMethod, duration, result.statusCode); return result; } catch (error) { const duration = Date.now() - startTime; const requestId = context?.awsRequestId; logger.error('Unhandled error in health endpoint', error, { duration }); metrics.recordError(error.name || 'UnknownError', '/api/health', event.httpMethod); return createErrorResponse( 500, 'Internal Server Error', 'An unexpected error occurred while processing the request', requestId, process.env.NODE_ENV === 'development' ? { stack: error.stack, originalError: error.message } : undefined ); } }; } /** * Main health check handler with structured logging */ const healthHandler = async (event, context, logger, metrics) => { // Handle OPTIONS request for CORS preflight if (event.httpMethod === 'OPTIONS') { logger.info('CORS preflight request handled'); return createSuccessResponse({}, 200, { 'Access-Control-Allow-Methods': 'GET,OPTIONS' }); } // Validate HTTP method if (event.httpMethod !== 'GET') { logger.warn('Invalid HTTP method attempted', { method: event.httpMethod, allowedMethods: ['GET', 'OPTIONS'] }); metrics.recordError('MethodNotAllowed', '/api/health', event.httpMethod); return createErrorResponse( 405, 'Method Not Allowed', `HTTP method ${event.httpMethod} is not allowed for this endpoint`, context?.awsRequestId ); } logger.info('Performing health checks'); // Perform basic health checks const memoryUsage = process.memoryUsage(); const uptime = process.uptime(); const healthData = { status: 'healthy', version: process.env.API_VERSION || '1.0.0', service: 'serverless-web-app-api', environment: process.env.NODE_ENV || 'production', checks: { memory: { used: memoryUsage.heapUsed, total: memoryUsage.heapTotal, external: memoryUsage.external, rss: memoryUsage.rss, status: memoryUsage.heapUsed < memoryUsage.heapTotal * 0.9 ? 'ok' : 'warning' }, uptime: { seconds: uptime, status: uptime > 0 ? 'ok' : 'error' } } }; // Add request context information if (event.requestContext) { healthData.requestContext = { requestId: event.requestContext.requestId, stage: event.requestContext.stage, httpMethod: event.requestContext.httpMethod, path: event.requestContext.path }; } // Log health check metrics metrics.recordMetric('MemoryUsage', memoryUsage.heapUsed, 'Bytes'); metrics.recordMetric('Uptime', uptime, 'Seconds'); logger.info('Health check completed successfully', { memoryUsed: memoryUsage.heapUsed, memoryTotal: memoryUsage.heapTotal, uptime: uptime }); return createSuccessResponse(healthData); }; // Export the wrapped handler exports.handler = withErrorHandling(healthHandler);