A utility to handle AWS Lambda timeouts gracefully by providing early detection and cleanup opportunities.
- Early timeout detection with configurable safety margin
- Support for user-provided cleanup handlers
- Graceful cleanup of resources before timeout
- Fallback timer mode for testing outside Lambda
- Configurable logging
npm install @hopdrive/lambda-timeout-wrapper
import { createTimeoutWrapper } from '@hopdrive/lambda-timeout-wrapper';
// Create a wrapper with Lambda context
const wrapper = createTimeoutWrapper({
getRemainingTimeInMillis: context.getRemainingTimeInMillis,
logger: console.log
});
// Use the wrapper
const result = await wrapper(
// Your main function
async () => {
// Your long-running code here
return { success: true };
},
// Timeout handler
async () => {
// Cleanup code when timeout is imminent
await cleanup();
},
// Optional user cleanup handler
async () => {
// Additional cleanup specific to your use case
await userCleanup();
}
);
import { withTimeout } from '@hopdrive/lambda-timeout-wrapper';
// Direct usage in Lambda handler with minimal configuration
export const handler = withTimeout(event, context, {
// Main function to run
run: async () => {
// Your main Lambda logic here
return { statusCode: 200, body: 'Success' };
},
// Cleanup handler - runs first when timeout occurs
onCleanup: async () => {
// Close any open resources
},
// Timeout handler - runs after cleanup when timeout is imminent
onTimeout: async () => {
// Your timeout handling logic
return { statusCode: 408, body: 'Request timed out' };
}
});
This ultra-simplified API makes it easier to use the wrapper directly in your Lambda handlers. The wrapper automatically:
- Gets the remaining time from the Lambda context
- Sets sensible defaults (safetyMarginMs: 1000, logger: console.log)
- Handles the proper execution order of handlers when a timeout occurs
The wrapper accepts the following options:
interface TimeoutWrapperOptions {
// Time in ms to start timeout handling before actual timeout (default: 5000)
safetyMarginMs?: number;
// Interval in ms to check for timeout (default: 1000)
checkIntervalMs?: number;
// Time in ms allocated for cleanup operations (default: 3000)
cleanupTimeMs?: number;
// Function to get remaining execution time in ms
getRemainingTimeInMillis?: () => number;
// Whether to use fallback timer instead of Lambda context
isUsingFallbackTimer?: boolean;
// Logger function for timeout wrapper messages
logger?: (message: string) => void;
}
The wrapper will throw a TimeoutError
when a timeout is detected. This error includes:
interface TimeoutError extends Error {
isLambdaTimeout: boolean;
handlerError?: Error;
}
For testing outside of Lambda, you can use the fallback timer mode:
const wrapper = createTimeoutWrapper({
isUsingFallbackTimer: true,
getRemainingTimeInMillis: () => 60000 // 60 seconds
});
# Clone the repository
git clone https://github.com/hopdrive/lambda-timeout-wrapper.git
cd lambda-timeout-wrapper
# Install dependencies
npm install
# Build all formats (CJS, ESM, and type declarations)
npm run build
# Build specific formats
npm run build:cjs
npm run build:esm
npm run build:types
# Run tests
npm test
# Standard deployment (specify version)
npm run deploy 1.0.1
# Force deployment (creates an empty commit, specify version)
npm run deploy-force 1.0.1
# Using the @hopdrive/package-deploy-scripts integration
npm run deploy-wrapper # Uses the wrapper around package-deploy-scripts
During deployment, the lib/version.js
file is automatically generated using the genversion
npm package. This file exports the current package version from package.json, making it accessible throughout the codebase.
This package includes several example implementations in the examples
directory:
File: examples/simple-lambda-example.js
A basic example showing how to use the wrapper in a standard AWS Lambda function without any framework dependencies.
Files:
- JavaScript:
examples/netlify-function-example.js
- TypeScript:
examples/netlify-function-example.ts
Shows how to use the wrapper in a Netlify Function, which runs on AWS Lambda, with database connection handling.
File: examples/local-fallback-example.js
Demonstrates how to use the wrapper with the fallback timer mode for local development and testing outside of a Lambda environment.
-
Install the package:
npm install @hopdrive/lambda-timeout-wrapper
-
For Netlify Functions:
- Create a
netlify/functions
directory in your project - Copy the example file and customize as needed
- Install TypeScript if using the TypeScript example:
npm install --save-dev typescript @netlify/functions
- Create a
netlify.toml
file to configure function settings:[build] functions = "netlify/functions" [functions] # Extend the timeout to 30 seconds (default is 10) timeout = 30
- Create a
-
Deploy to AWS or Netlify:
- Follow standard AWS Lambda or Netlify deployment procedures
- Your functions will now gracefully handle timeouts
-
Install the package:
npm install @hopdrive/lambda-timeout-wrapper
-
Run the local example:
node examples/local-fallback-example.js
-
Customize timeout values:
- Adjust
getRemainingTimeInMillis
to simulate different Lambda timeouts - Modify the simulated task duration to test timeout behavior
- Adjust
const wrapper = createTimeoutWrapper({
// Get remaining time from Lambda context (for Lambda/Netlify)
getRemainingTimeInMillis: context.getRemainingTimeInMillis,
// OR use fallback timer for local testing
// isUsingFallbackTimer: true,
// getRemainingTimeInMillis: () => 10000, // 10 seconds
// Set safety margin (how early to detect timeout)
safetyMarginMs: 3000, // 3 seconds
// Enable logging
logger: console.log
});
const result = await wrapper(
// Main function - your primary logic
async () => {
// Do your work here
return { success: true };
},
// Timeout handler - runs when timeout is imminent
async () => {
// Handle timeout gracefully
return { timedOut: true };
},
// User cleanup handler - always runs after success or timeout
async () => {
// Clean up resources here (database connections, etc.)
}
);
try {
const result = await wrapper(/* ... */);
return result;
} catch (error) {
// Check if it's a timeout error
if (error.isLambdaTimeout) {
// Handle timeout-specific error case
}
// Handle other errors
}
-
Directory Structure:
your-project/ ├── netlify.toml └── netlify/ └── functions/ └── your-function.js
-
Required Dependencies:
- For JavaScript:
npm install @hopdrive/lambda-timeout-wrapper
- For TypeScript:
npm install @hopdrive/lambda-timeout-wrapper npm install --save-dev typescript @netlify/functions @types/node
- For JavaScript:
-
Running Locally:
npm install -g netlify-cli netlify dev
-
Testing: Access your function at:
http://localhost:8888/.netlify/functions/your-function
To test timeout behavior:
-
Increase task duration:
- Modify the setTimeout duration in the examples to exceed the timeout limit
-
Decrease safety margin:
- Set a smaller safetyMarginMs value to test different cleanup timing
-
Observe graceful shutdown:
- Watch how resources are properly cleaned up when timeout occurs
MIT