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.
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';
}
}));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;
}
}
}));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');
}
}));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
});
}
}));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: Executed first, before any proxy processing beginsfilter returns trueskipToNextHandlerFilter: Executed after receiving proxy responseapp.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
}
}));Note that defining skipToNextHandlerFilter disables streaming mode, which may impact performance with large payloads since responses must be buffered for inspection.
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
}
}
}));