or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdevents.mdindex.mdrequests.mdservices.mdutilities.md
tile.json

events.mddocs/

Events & Lifecycle

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.

Event System Architecture

AWS.SequentialExecutor Class

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

Global Event System

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

Request Lifecycle Events

Event Sequence

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

Request-Level Event Handlers

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

Global Event Handlers

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

Streaming Events

HTTP Data Events

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

Progress Tracking

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

Retry Events

Retry Monitoring

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

Custom Event Implementation

Named Event Listeners

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

Service-Level Event Handlers

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

Advanced Event Patterns

Conditional Event Handling

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

Event Chaining

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

Error Recovery with Events

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

Event-Based Middleware

Request Transformation Middleware

// 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());

Response Processing Middleware

// 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());

Event Performance Considerations

Event Listener Management

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

Async Event Error Handling

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