tryError Documentation

Middleware System

Extend tryError with custom logic using a powerful middleware pipeline

Basic Concepts

Middleware functions intercept TryResult values and can modify them, log them, or perform side effects before passing control to the next middleware.

1import { ErrorMiddleware, TryResult, TryError } from '@try-error/core';
2
3// Basic middleware signature
4type ErrorMiddleware<T = any, E extends TryError = TryError> = (
5  result: TryResult<T, E>,
6  next: () => TryResult<T, E>
7) => TryResult<T, E>;
8
9// Example: Simple logging middleware
10const loggingMiddleware: ErrorMiddleware = (result, next) => {
11  if (isTryError(result)) {
12    console.error('Error occurred:', result);
13  }
14  return next(); // Pass control to next middleware
15};

Using Middleware

1import { MiddlewarePipeline } from '@try-error/core';
2
3// Create a middleware pipeline
4const pipeline = new MiddlewarePipeline();
5
6// Add middleware to the pipeline
7pipeline
8  .use(loggingMiddleware)
9  .use(retryMiddleware(3))
10  .use(transformMiddleware);
11
12// Execute with a result
13const result = pipeline.execute(trySync(() => riskyOperation()));
14
15// Or wrap a function
16const safeFn = pipeline.wrap((x: number) => 
17  trySync(() => {
18    if (x < 0) throw new Error('Negative number');
19    return Math.sqrt(x);
20  })
21);
22
23const result = safeFn(16); // Middleware applied automatically

Built-in Middleware

tryError provides a collection of common middleware patterns ready to use.

Logging Middleware
Log errors to console or external services
import { loggingMiddleware } from '@try-error/core';

// Simple console logging
pipeline.use(loggingMiddleware(console.error));

// Custom logger
const customLogger = (error: TryError) => {
  myLoggingService.log({
    level: 'error',
    message: error.message,
    type: error.type,
    context: error.context,
    timestamp: error.timestamp
  });
};

pipeline.use(loggingMiddleware(customLogger));
Retry Middleware
Automatically retry failed operations
import { retryMiddleware } from '@try-error/core';

// Retry up to 3 times
pipeline.use(retryMiddleware(3));

// With custom retry logic
const shouldRetry = (error: TryError) => {
  // Only retry network errors
  return error.type === 'NetworkError' && 
         error.context?.statusCode !== 404;
};

pipeline.use(retryMiddleware(5, shouldRetry));

// Note: Retry middleware tracks attempts internally
// Each call to the pipeline counts as one attempt
Transform Middleware
Modify errors before they're returned
import { transformMiddleware } from '@try-error/core';

// Add additional context
const addRequestId = transformMiddleware(error => ({
  ...error,
  context: {
    ...error.context,
    requestId: generateRequestId()
  }
}));

// Sanitize sensitive data
const sanitize = transformMiddleware(error => ({
  ...error,
  message: error.message.replace(/password=S+/g, 'password=***'),
  context: omit(error.context, ['password', 'apiKey'])
}));

pipeline.use(addRequestId).use(sanitize);
Circuit Breaker Middleware
Prevent cascading failures in distributed systems
import { circuitBreakerMiddleware } from '@try-error/core';

const circuitBreaker = circuitBreakerMiddleware({
  threshold: 5,        // Open after 5 failures
  timeout: 60000,      // Try again after 1 minute
  onOpen: () => {
    console.warn('Circuit breaker opened!');
    alertOpsTeam();
  },
  onClose: () => {
    console.info('Circuit breaker closed');
  }
});

pipeline.use(circuitBreaker);

// When circuit is open, requests fail fast with:
// { type: 'CircuitBreakerOpen', message: 'Circuit breaker is open' }
Rate Limit Middleware
Limit error frequency to prevent spam
import { rateLimitMiddleware } from '@try-error/core';

// Allow max 100 errors per second
const rateLimiter = rateLimitMiddleware(1000, 100);

pipeline.use(rateLimiter);

// Errors beyond limit return:
// { 
//   type: 'RateLimitExceeded',
//   message: 'Rate limit exceeded: 100 errors in 1000ms',
//   context: { windowMs: 1000, maxErrors: 100, currentCount: 100 }
// }
Context Enrichment
Add contextual information to errors
import { enrichContextMiddleware } from '@try-error/core';

// Add static context
pipeline.use(enrichContextMiddleware(() => ({
  service: 'api-gateway',
  version: process.env.APP_VERSION,
  region: process.env.AWS_REGION
})));

// Add dynamic context
pipeline.use(enrichContextMiddleware(() => ({
  timestamp: Date.now(),
  requestId: getCurrentRequestId(),
  userId: getCurrentUserId(),
  memory: process.memoryUsage()
})));

Creating Custom Middleware

Build your own middleware to handle specific requirements.

1// Send errors to monitoring service
2const monitoringMiddleware: ErrorMiddleware = (result, next) => {
3  if (isTryError(result)) {
4    // Non-blocking send to monitoring
5    sendToMonitoring({
6      error: {
7        type: result.type,
8        message: result.message,
9        stack: result.stack,
10        context: result.context
11      },
12      metadata: {
13        service: 'my-service',
14        timestamp: result.timestamp,
15        severity: getSeverity(result.type)
16      }
17    }).catch(console.error); // Don't let monitoring fail the request
18  }
19  
20  return next();
21};
22
23// Metrics collection
24const metricsMiddleware: ErrorMiddleware = (result, next) => {
25  const start = performance.now();
26  const nextResult = next();
27  const duration = performance.now() - start;
28  
29  if (isTryError(nextResult)) {
30    metrics.increment('errors.total', {
31      type: nextResult.type,
32      duration: Math.round(duration)
33    });
34  }
35  
36  return nextResult;
37};

Best Practices

Middleware Order Matters

Middleware executes in the order it's added:

// ❌ Wrong order - retry happens after transform
pipeline
  .use(transformMiddleware)
  .use(retryMiddleware(3));

// ✅ Correct order - retry original operation
pipeline
  .use(retryMiddleware(3))
  .use(transformMiddleware);
Always Call Next
// ❌ Forgetting to call next breaks the chain
const badMiddleware = (result, next) => {
  if (isTryError(result)) {
    console.error(result);
    // Missing: return next();
  }
  return result; // Wrong!
};

// ✅ Always call next or return a result
const goodMiddleware = (result, next) => {
  if (isTryError(result)) {
    console.error(result);
  }
  return next(); // Continue the chain
};
Handle Middleware Errors
// Use trySync to safely handle middleware operations
const safeMiddleware: ErrorMiddleware = (result, next) => {
  if (isTryError(result)) {
    // Safely call external service
    const logResult = trySync(() => {
      externalService.logError(result);
    });
    
    if (isTryError(logResult)) {
      // Don't let middleware errors break the app
      console.error('Middleware error:', logResult.message);
    }
  }
  
  return next();
};