or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

body-processing.mderror-handling.mdfiltering.mdindex.mdnetwork-configuration.mdpath-resolution.mdrequest-modification.mdresponse-processing.md
tile.json

filtering.mddocs/

Request Filtering

Control which requests get proxied and how proxy responses are handled using filter functions and conditional logic. The filtering system supports both synchronous and Promise-based async operations.

Capabilities

Request Filter

Determine whether a request should be proxied. Return true to continue with proxying, or false to skip proxying and call next().

/**
 * Filter function to determine whether to proxy the request
 * @param {object} req - Express request object
 * @param {object} res - Express response object
 * @returns {boolean|Promise<boolean>} True to proxy, false to skip
 */
filter: (req, res) => boolean | Promise<boolean>;

Usage Examples:

const proxy = require('express-http-proxy');

// Method-based filtering
app.use('/api', proxy('backend.example.com', {
  filter: function(req, res) {
    // Only proxy GET requests
    return req.method === 'GET';
  }
}));

// Authentication-based filtering
app.use('/secure', proxy('secure-api.com', {
  filter: function(req, res) {
    // Only proxy authenticated requests
    return req.headers.authorization && req.user && req.user.isAuthenticated;
  }
}));

// Path-based filtering
app.use('/conditional', proxy('api.example.com', {
  filter: function(req, res) {
    // Skip certain paths
    const skipPaths = ['/health', '/metrics', '/status'];
    return !skipPaths.some(path => req.path.startsWith(path));
  }
}));

// Header-based filtering
app.use('/versioned', proxy('api-v2.example.com', {
  filter: function(req, res) {
    // Only proxy requests with specific API version
    return req.headers['api-version'] === '2.0';
  }
}));

Promise-Based Request Filtering

Use Promises for asynchronous filtering operations that require database lookups, external service calls, or other async operations.

/**
 * Promise-based filter function for async filtering logic
 * @param {object} req - Express request object
 * @param {object} res - Express response object
 * @returns {Promise<boolean>} Promise resolving to true (proxy) or false (skip)
 */
filter: (req, res) => Promise<boolean>;

Usage Examples:

// Database-driven filtering
app.use('/restricted', proxy('restricted-api.com', {
  filter: async function(req, res) {
    const userId = req.user?.id;
    if (!userId) return false;
    
    // Check user permissions in database
    const hasAccess = await db.checkUserAccess(userId, 'restricted-api');
    return hasAccess;
  }
}));

// Rate limiting with async check
app.use('/throttled', proxy('api.example.com', {
  filter: function(req, res) {
    return new Promise((resolve) => {
      const clientId = req.headers['x-client-id'];
      
      rateLimiter.checkLimit(clientId)
        .then(withinLimit => {
          resolve(withinLimit);
        })
        .catch(() => {
          // Default to allow on rate limiter errors
          resolve(true);
        });
    });
  }
}));

// External service validation
app.use('/validated', proxy('validated-api.com', {
  filter: async function(req, res) {
    try {
      const token = req.headers.authorization?.replace('Bearer ', '');
      const isValid = await authService.validateToken(token);
      return isValid;
    } catch (error) {
      console.error('Token validation error:', error);
      return false;
    }
  }
}));

Skip to Next Handler Filter

Inspect the proxy response and decide whether to continue processing through express-http-proxy or call next() to return control to Express.

/**
 * Inspect proxy response and decide whether to continue processing
 * @param {object} proxyRes - The proxy response object
 * @returns {boolean|Promise<boolean>} True to skip to next handler, false to continue processing
 */
skipToNextHandlerFilter: (proxyRes) => boolean | Promise<boolean>;

Usage Examples:

// Status code-based skipping
app.use('/fallback', proxy('primary-api.com', {
  skipToNextHandlerFilter: function(proxyRes) {
    // Skip to next handler for 404 responses
    return proxyRes.statusCode === 404;
  }
}));

// Handle specific error conditions
app.use('/resilient', proxy('unreliable-api.com', {
  skipToNextHandlerFilter: function(proxyRes) {
    // Skip processing for server errors and timeouts
    return proxyRes.statusCode >= 500 || proxyRes.statusCode === 408;
  }
}));

// Content-type based routing
app.use('/dynamic', proxy('api.example.com', {
  skipToNextHandlerFilter: function(proxyRes) {
    // Let Express handle non-JSON responses
    const contentType = proxyRes.headers['content-type'];
    return !contentType || !contentType.includes('application/json');
  }
}));

Promise-Based Skip Filter

Use Promises for asynchronous decision-making based on proxy response analysis.

/**
 * Promise-based skip filter for async response analysis
 * @param {object} proxyRes - The proxy response object
 * @returns {Promise<boolean>} Promise resolving to true (skip) or false (continue)
 */
skipToNextHandlerFilter: (proxyRes) => Promise<boolean>;

Usage Examples:

// Async response validation
app.use('/validated-response', proxy('api.example.com', {
  skipToNextHandlerFilter: async function(proxyRes) {
    // Skip if response doesn't meet quality criteria
    if (proxyRes.statusCode !== 200) return true;
    
    try {
      // Validate response with external service
      const isValidResponse = await responseValidator.validate(proxyRes);
      return !isValidResponse; // Skip if invalid
    } catch (error) {
      console.error('Response validation error:', error);
      return true; // Skip on validation errors
    }
  }
}));

// Circuit breaker pattern
app.use('/circuit-breaker', proxy('monitored-api.com', {
  skipToNextHandlerFilter: function(proxyRes) {
    return new Promise((resolve) => {
      // Check circuit breaker status
      circuitBreaker.shouldSkip(proxyRes.statusCode)
        .then(shouldSkip => {
          if (shouldSkip) {
            console.log('Circuit breaker activated, skipping proxy');
          }
          resolve(shouldSkip);
        })
        .catch(() => resolve(false)); // Continue on error
    });
  }
}));

Filter Behavior and Promise Handling

Success Path vs Error Path

For both filter and skipToNextHandlerFilter:

  • resolve(false) or return false: Executes the "happy path" (continue processing)
  • resolve(true) or return true: Skips the rest of proxy processing and calls next()
  • reject() or thrown error: Also skips proxy processing and calls next()

Filter Execution Order

  1. filter: Executed first, before any proxy processing begins
  2. Proxy request processing: Only if filter returns true
  3. skipToNextHandlerFilter: Executed after receiving proxy response

Combining Filters

app.use('/complex', proxy('api.example.com', {
  // Pre-request filtering
  filter: async function(req, res) {
    // Check authentication
    if (!req.user) return false;
    
    // Check rate limits
    const withinLimits = await rateLimiter.check(req.user.id);
    return withinLimits;
  },
  
  // Post-response filtering
  skipToNextHandlerFilter: function(proxyRes) {
    // Handle errors by skipping to Express error handling
    if (proxyRes.statusCode >= 400) {
      console.log('Proxy returned error status:', proxyRes.statusCode);
      return true; // Skip to next handler
    }
    return false; // Continue processing
  }
}));

Performance Considerations

Streaming Impact

Note that defining skipToNextHandlerFilter disables streaming mode, which may impact performance with large payloads since responses must be buffered for inspection.

Error Handling

app.use('/error-handling', proxy('api.example.com', {
  filter: function(req, res) {
    try {
      // Your filtering logic here
      return someConditionCheck(req);
    } catch (error) {
      console.error('Filter error:', error);
      return false; // Default to not proxying on error
    }
  }
}));