The AWS SDK provides a comprehensive event system built on the SequentialExecutor pattern, enabling custom behavior injection throughout the request lifecycle and global event handling across all services.
class SequentialExecutor {
// Event registration
on(eventName: string, listener: Function, toHead?: boolean): SequentialExecutor;
addListener(eventName: string, listener: Function, toHead?: boolean): SequentialExecutor;
onAsync(eventName: string, listener: Function, toHead?: boolean): SequentialExecutor;
addAsyncListener(eventName: string, listener: Function, toHead?: boolean): SequentialExecutor;
// Event removal
removeListener(eventName: string, listener: Function): SequentialExecutor;
removeAllListeners(eventName?: string): SequentialExecutor;
// Event emission
emit(eventName: string, eventObject?: any, callback?: Function): void;
emitSync(eventName: string, eventObject?: any): void;
// Named listeners (for easier removal)
addNamedListener(name: string, eventName: string, listener: Function, toHead?: boolean): SequentialExecutor;
addNamedAsyncListener(name: string, eventName: string, listener: Function, toHead?: boolean): SequentialExecutor;
removeNamedListener(name: string): SequentialExecutor;
}const AWS = require('aws-sdk');
// Global event emitter for all SDK operations
const globalEvents = AWS.events; // SequentialExecutor instance
// Listen to events across all services and requests
AWS.events.on('send', (response) => {
console.log('Sending request:', response.request.operation);
});
AWS.events.on('complete', (response) => {
console.log('Request completed:', response.request.operation);
});The AWS SDK request lifecycle follows this event sequence:
const requestLifecycleEvents = [
'validate', // Parameter validation (synchronous)
'build', // Request building (synchronous)
'afterBuild', // Post-build customization (synchronous)
'sign', // Request signing (synchronous)
'send', // HTTP transmission begins (asynchronous)
'validateResponse', // Response validation (synchronous)
'extractData', // Data extraction from response (synchronous)
'success', // Successful request completion (synchronous)
'error', // Error occurred (synchronous)
'complete' // Request lifecycle end (synchronous)
];
const streamingEvents = [
'httpData', // HTTP data chunk received (asynchronous)
'httpUploadProgress', // Upload progress update (asynchronous)
'httpDownloadProgress', // Download progress update (asynchronous)
'httpDone', // HTTP transmission complete (asynchronous)
'retry' // Retry attempt (asynchronous)
];Basic Event Listening:
const s3 = new AWS.S3();
const request = s3.getObject({ Bucket: 'my-bucket', Key: 'my-file.txt' });
// Listen to request lifecycle events
request.on('validate', (req) => {
console.log('Validating parameters for:', req.operation);
});
request.on('build', (req) => {
console.log('Building request:', req.operation);
// Modify request if needed
req.httpRequest.headers['Custom-Header'] = 'custom-value';
});
request.on('sign', (req) => {
console.log('Signing request with access key:', req.service.config.credentials?.accessKeyId);
});
request.on('send', (resp) => {
console.log('Sending HTTP request to:', resp.request.httpRequest.endpoint.href);
resp.startTime = Date.now();
});
request.on('success', (resp) => {
console.log('Request successful. Request ID:', resp.requestId);
});
request.on('error', (err, resp) => {
console.log('Request failed:', err.code, err.message);
});
request.on('complete', (resp) => {
const duration = Date.now() - resp.startTime;
console.log(`Request completed in ${duration}ms`);
});
request.send();Async Event Handling:
// Async event handlers for I/O operations
request.onAsync('send', (resp, done) => {
// Log request details to external service
logService.logRequest(resp.request.operation, (err) => {
done(); // Continue with request
});
});
request.onAsync('complete', (resp, done) => {
// Upload metrics to monitoring service
metricsService.recordMetric({
operation: resp.request.operation,
statusCode: resp.httpResponse?.statusCode,
duration: Date.now() - resp.startTime
}, done);
});Cross-Service Event Handling:
// Monitor all SDK requests globally
AWS.events.on('send', (resp) => {
resp.startTime = Date.now();
console.log(`Starting ${resp.request.service.serviceIdentifier}.${resp.request.operation}`);
});
AWS.events.on('complete', (resp) => {
const duration = Date.now() - resp.startTime;
const service = resp.request.service.serviceIdentifier;
const operation = resp.request.operation;
console.log(`${service}.${operation} completed in ${duration}ms`);
// Log slow requests
if (duration > 5000) {
console.warn(`Slow request detected: ${service}.${operation} took ${duration}ms`);
}
});
// Global error handling
AWS.events.on('error', (err, resp) => {
console.error(`Global error in ${resp.request.service.serviceIdentifier}.${resp.request.operation}:`, err.code);
// Send error to monitoring service
if (err.statusCode >= 500) {
errorReporting.reportError(err, resp.request);
}
});const request = s3.getObject({ Bucket: 'my-bucket', Key: 'large-file.zip' });
// Monitor data transmission
request.on('httpData', (chunk, resp) => {
console.log(`Received ${chunk.length} bytes`);
// Process data chunk
processDataChunk(chunk);
});
request.on('httpDone', (resp) => {
console.log('HTTP transmission complete');
});
request.send();// Upload progress monitoring
const uploadRequest = s3.upload({
Bucket: 'my-bucket',
Key: 'large-file.zip',
Body: fs.createReadStream('large-file.zip')
});
uploadRequest.on('httpUploadProgress', (progress) => {
const percent = Math.round((progress.loaded / progress.total) * 100);
console.log(`Upload progress: ${percent}% (${progress.loaded}/${progress.total} bytes)`);
// Update progress bar
updateProgressBar(percent);
});
// Download progress monitoring
const downloadRequest = s3.getObject({ Bucket: 'my-bucket', Key: 'large-file.zip' });
downloadRequest.on('httpDownloadProgress', (progress) => {
const percent = Math.round((progress.loaded / progress.total) * 100);
console.log(`Download progress: ${percent}% (${progress.loaded}/${progress.total} bytes)`);
});const request = s3.getObject({ Bucket: 'my-bucket', Key: 'my-file.txt' });
request.on('retry', (resp) => {
console.log(`Retry attempt #${resp.retryCount}`);
console.log(`Error: ${resp.error?.code} - ${resp.error?.message}`);
console.log(`Retry delay: ${resp.error?.retryDelay}ms`);
// Custom retry logic
if (resp.retryCount >= 3 && resp.error?.code === 'ThrottlingException') {
console.log('Too many throttling errors, implementing exponential backoff');
}
});
request.send();const s3 = new AWS.S3();
// Add named listeners for easier management
s3.addNamedListener('REQUEST_LOGGER', 'send', (resp) => {
console.log('Logging request:', resp.request.operation);
});
s3.addNamedListener('RESPONSE_CACHE', 'success', (resp) => {
// Cache successful responses
responseCache.set(resp.request.operation, resp.data);
});
// Remove named listener
s3.removeNamedListener('REQUEST_LOGGER');// Apply events to all requests for a service instance
const s3 = new AWS.S3();
s3.on('build', (req) => {
// Add custom headers to all S3 requests
req.httpRequest.headers['X-Custom-Header'] = 'my-value';
});
s3.on('send', (resp) => {
// Log all S3 operations
console.log('S3 operation:', resp.request.operation);
});AWS.events.on('send', (resp) => {
const service = resp.request.service.serviceIdentifier;
const operation = resp.request.operation;
// Handle specific service operations
if (service === 's3' && operation === 'putObject') {
console.log('S3 upload initiated');
resp.uploadStartTime = Date.now();
}
if (service === 'dynamodb') {
console.log('DynamoDB operation:', operation);
// Add custom DynamoDB headers
resp.request.httpRequest.headers['X-DynamoDB-Target'] = `DynamoDB_20120810.${operation}`;
}
});const request = s3.getObject({ Bucket: 'my-bucket', Key: 'data.json' });
request
.on('build', (req) => {
console.log('1. Building request');
})
.on('sign', (req) => {
console.log('2. Signing request');
})
.on('send', (resp) => {
console.log('3. Sending request');
})
.on('success', (resp) => {
console.log('4. Request successful');
})
.on('complete', (resp) => {
console.log('5. Request complete');
});
request.send();const request = s3.getObject({ Bucket: 'my-bucket', Key: 'my-file.txt' });
request.on('error', (err, resp) => {
if (err.code === 'NoSuchKey') {
console.log('File not found, creating default file');
// Create default file
s3.putObject({
Bucket: 'my-bucket',
Key: 'my-file.txt',
Body: 'Default content'
}, (putErr, putData) => {
if (!putErr) {
console.log('Default file created, retrying original request');
// Retry original request
request.send();
}
});
}
});
request.send();// Middleware to transform requests
function addRequestIdMiddleware() {
return function(req) {
req.httpRequest.headers['X-Request-ID'] = AWS.util.uuid.v4();
};
}
function addTimestampMiddleware() {
return function(req) {
req.httpRequest.headers['X-Timestamp'] = new Date().toISOString();
};
}
// Apply middleware globally
AWS.events.on('build', addRequestIdMiddleware());
AWS.events.on('build', addTimestampMiddleware());// Middleware to process responses
function responseLoggerMiddleware() {
return function(resp) {
console.log(`Response from ${resp.request.service.serviceIdentifier}.${resp.request.operation}`);
console.log('Status Code:', resp.httpResponse?.statusCode);
console.log('Request ID:', resp.requestId);
};
}
function responseCacheMiddleware() {
const cache = new Map();
return function(resp) {
const cacheKey = `${resp.request.service.serviceIdentifier}.${resp.request.operation}`;
cache.set(cacheKey, {
data: resp.data,
timestamp: Date.now(),
requestId: resp.requestId
});
};
}
// Apply response middleware
AWS.events.on('success', responseLoggerMiddleware());
AWS.events.on('success', responseCacheMiddleware());// Efficient event listener management
const listeners = new Map();
function addRequestTracker(service) {
const listener = (resp) => {
console.log(`${service.serviceIdentifier} request:`, resp.request.operation);
};
service.on('send', listener);
listeners.set(service, listener);
}
function removeRequestTracker(service) {
const listener = listeners.get(service);
if (listener) {
service.removeListener('send', listener);
listeners.delete(service);
}
}
// Use for specific service instances
const s3 = new AWS.S3();
addRequestTracker(s3);
// Clean up when done
removeRequestTracker(s3);// Proper error handling in async events
request.onAsync('send', (resp, done) => {
externalService.logRequest(resp.request.operation)
.then(() => {
console.log('Request logged successfully');
done(); // Continue request
})
.catch((err) => {
console.error('Failed to log request:', err);
done(); // Continue request despite logging failure
});
});