Express middleware for proxying HTTP requests to another host and passing the response back to the original caller
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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
}
}
}));