
Research
Supply Chain Attack on Axios Pulls Malicious Dependency from npm
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.
responseinterceptor
Advanced tools
This package allows intercept an express response ( res.send , res.end , res.write ) and update or upgrade response with your other logic
This package is Middleware for Express responses. It allows you to intercept an Express response (res.send, res.end, res.write, res.render, etc.) and update or upgrade the response with your custom logic.
res.send(), res.json(), res.render(), etc.)etag for ETag support, propertiesmanager optional)To use responseinterceptor install it in your project by typing:
$ npm install responseinterceptor
Just require it like a simple package:
const responseinterceptor = require('responseinterceptor');
To intercept all routes, use it in app.js before all app.use() route functions:
const express = require('express');
const responseinterceptor = require('responseinterceptor');
const routeTwo = require("./routes/routeTwo");
const app = express();
app.use(responseinterceptor.intercept(function(body, bodyContentType, request, callback){
// Your custom logic after intercepting the response
// Example: Add timestamp to response body
let newResponse = body;
if(bodyContentType === "application/json") // Check if response is JSON
newResponse.otherInformation = Date.now(); // Add current timestamp
callback(newResponse); // Return modified response
}));
// All routes defined below this middleware will be intercepted
app.use("exampleRoute_one", function(req, res, next){
// your logic
});
app.use("exampleRoute_Two", routeTwo);
// Other routes...
You can decide which routes to intercept using or not using Express router. Read below for more details.
To intercept only a group of routes, use responseinterceptor middleware after the routes that should not be intercepted and before all routes to intercept. For example, intercept all routes matching "/intercept/onlythis":
const express = require('express');
const responseinterceptor = require('responseinterceptor');
const app = express();
// ############ Group of routes to not intercept ############
app.get("/", function(req, res, next){
// your logic
});
app.post("/", function(req, res, next){
// your logic
});
app.get("/intercept", function(req, res, next){
// your logic
});
// Additional routes that will NOT be intercepted...
// ############ Group of routes to intercept ############
// Place responseinterceptor middleware before all routes you want to intercept
app.use(responseinterceptor.intercept(function(body, bodyContentType, request, callback){
// Your custom logic after intercepting the response
// Example: Add timestamp to response body
let newResponse = body;
if(bodyContentType === "application/json") // Check if response is JSON
newResponse.otherInformation = Date.now(); // Add current timestamp
callback(newResponse); // Return modified response
}));
app.get("/intercept/onlythis", function(req, res, next){
// your logic
res.send({data: "...."}); // This response WILL be intercepted
});
app.post("/intercept/onlythis", function(req, res, next){
// your logic
res.send({data: "...."}); // This response WILL be intercepted
});
// Additional routes that WILL be intercepted...
To intercept only a group of routes using Express routing, use responseinterceptor middleware in a separate Express routing file. For example, intercept all routes under "/intercept".
Define a router for "/intercept", for example in file routeInt.js:
const express = require('express');
const router = express.Router();
const responseinterceptor = require('responseinterceptor');
// Place responseinterceptor middleware before all routes you want to intercept
router.use(responseinterceptor.intercept(function(body, bodyContentType, request, callback){
// Your custom logic after intercepting the response
// Example: Add timestamp to response body
let newResponse = body;
if(bodyContentType === "application/json") // Check if response is JSON
newResponse.otherInformation = Date.now(); // Add current timestamp
callback(newResponse); // Return modified response
}));
// define routes to intercept
router.get("/", function(req, res, next){
// your logic
res.send({data: "...."}); // This response WILL be intercepted
});
router.post("/alsothis", function(req, res, next){
// your logic
res.send({data: "...."}); // This response WILL be intercepted
});
// Additional routes to intercept...
module.exports = router;
In app.js use the "/intercept" route:
const express = require('express');
const routeInt = require('./routes/routeInt');
const app = express();
app.use('/intercept', routeInt);
To intercept a single route response, use responseinterceptor not as a global level middleware but at the single endpoint definition level, as middleware or in the endpoint logic.
You might think that using responseinterceptor at the endpoint level makes no sense because you could include the interceptor logic directly before sending the response. However, this is not always true. There are real use cases where you need to do it:
write/end function (for example, fields like etag, content-length, etc. are calculated in write/end after res.send() is called).res.render()).To intercept a single route, use responseinterceptor middleware in the endpoint route definition. For example, intercept all "/intercept/" routes in POST method but not in GET method:
const express = require('express');
const app = express();
const responseinterceptor = require('responseinterceptor');
// Route without interception
app.get("/", function(req, res, next){
});
// Route with interception applied via middleware
app.post("/", middlewareInterceptor, function(req, res, next){
// Your Logic
res.send({data: "...."}); // This response WILL be intercepted
});
const middlewareInterceptor = responseinterceptor.intercept(function(body, bodyContentType, request, callback){
// Your custom logic after intercepting the response
// Example: Add timestamp to response body
let newResponse = body;
if(bodyContentType === "application/json") // Check if response is JSON
newResponse.otherInformation = Date.now(); // Add current timestamp
callback(newResponse); // Return modified response
});
// This route is NOT intercepted because the middleware is applied at route level,
// not globally. Only routes explicitly using middlewareInterceptor will be intercepted.
app.get("/notInterceptable", function(req, res, next){
// Your Logic
res.send({data: "...."}); // This response will NOT be intercepted
});
// Another route with interception
app.post("/interceptable", middlewareInterceptor, function(req, res, next){
// Your Logic
res.send({data: "...."}); // This response WILL be intercepted
});
To intercept a single route using responseinterceptor not as middleware but only if a particular condition in the endpoint logic is satisfied, you should use the interceptOnFly function. For example, intercept a route "/intercept" in GET method only if the request has a field "intercept" set to true:
const express = require('express');
const app = express();
const responseinterceptor = require('responseinterceptor');
// Route without interception
app.get("/", function(req, res, next){
});
// Conditionally intercept based on request parameter
app.get("/intercept", function(req, res, next){
// Your Logic
if(req.query.intercept == true){
responseinterceptor.interceptOnFly(req, res, function(body, bodyContentType, request, callback){
// Your custom logic after intercepting the response
// newResponse = ...
callback(newResponse); // Return modified response
});
}
// Additional logic...
// This response will be intercepted ONLY if req.query.intercept is true
res.send({data: "...."});
});
This middleware overrides res.end() to detect when the response status matches one of the specified status codes. If a match occurs, it executes a user-defined callback instead of sending the original response.
const express = require('express');
const app = express();
const responseinterceptor = require('responseinterceptor');
// Intercept all 403 Forbidden responses
app.use(responseinterceptor.interceptByStatusCode(403, (req, respond) => {
app.render('access-denied', {}, (err, html) => {
if (!err) {
// Template rendered successfully - send custom HTML page
respond(200, html);
} else {
// Fallback if template fails - send simple HTML message
respond(200, '<h1>Access Denied</h1><p>You are not authorized to view this page.</p>');
}
});
}));
// Intercept 404 with JSON response (auto-detected)
app.use(responseinterceptor.interceptByStatusCode(404, (req, respond) => {
respond(404, { error: 'Not Found', path: req.path });
}));
// Intercept 500 with explicit XML content-type
app.use(responseinterceptor.interceptByStatusCode(500, (req, respond) => {
respond(500, '<error>Internal Server Error</error>', 'application/xml; charset=utf-8');
}));
// Example route that triggers the interception
app.get('/private', (req, res) => {
res.status(403).send('Forbidden');
});
This middleware overrides res.end() to detect when the response status matches one of the specified status codes. If a match occurs, it executes a user-defined callback to redirect to a URL.
const express = require('express');
const app = express();
const responseinterceptor = require('responseinterceptor');
// Intercept all 403 Forbidden responses and redirect dynamically
app.use(responseinterceptor.interceptByStatusCodeRedirectTo(403, (req, respond) => {
respond('/index');
}));
// Example route that triggers interception and redirects to /index
app.get('/private', (req, res) => {
res.status(403).send('Forbidden');
});
This middleware overrides res.end() to detect when the response status matches one of the specified status codes. If a match occurs, it redirects to a static URL.
const express = require('express');
const app = express();
const responseinterceptor = require('responseinterceptor');
// Intercept all 403 Forbidden responses and redirect to static URL
app.use(responseinterceptor.interceptByStatusCodeRedirectTo(403, '/index'));
// Example route that triggers interception and redirects to /index
app.get('/private', (req, res) => {
res.status(403).send('Forbidden');
});
configure(options)Configure global options for responseinterceptor. This allows customization of logging and error handling behavior.
Parameters:
options - Configuration object
options.logging - Logging configuration
options.logging.enabled - Boolean to enable/disable logging (default: false in production, true otherwise)options.logging.logger - Custom logger function (default: console.log)options.errorHandling - Error handling configuration
options.errorHandling.rethrow - Boolean to rethrow errors (default: false)options.errorHandling.onError - Custom error handler function (err, req, res) => {}Returns: Current configuration object
Example:
const { configure } = require('responseinterceptor');
// Configure with Winston logger
const winston = require('winston');
const logger = winston.createLogger({ /* ... */ });
configure({
logging: {
enabled: true,
logger: (...args) => logger.info(args.join(' '))
},
errorHandling: {
rethrow: false,
onError: (err, req, res) => {
logger.error('Interceptor error:', {
error: err.message,
path: req.path,
method: req.method
});
// Send to monitoring service
// Sentry.captureException(err);
}
}
});
// Dynamic logging control
app.get('/admin/logging/toggle', (req, res) => {
const config = configure({
logging: { enabled: !currentlyEnabled }
});
res.json({ logging: config.logging.enabled });
});
Integration with PropertiesManager:
PropertiesManager automatically loads config/default.json with environment-based configuration:
// config/default.json structure:
{
"production": {
"responseinterceptor": {
"logging": { "enabled": false },
"errorHandling": { "rethrow": false }
}
},
"test": {
"responseinterceptor": {
"logging": { "enabled": false },
"errorHandling": { "rethrow": true }
}
},
"dev": {
"responseinterceptor": {
"logging": { "enabled": true },
"errorHandling": { "rethrow": false }
}
}
}
Usage:
const propertiesmanager = require('propertiesmanager').conf;
const { configure, getConfig } = require('responseinterceptor');
// PropertiesManager automatically selects environment based on NODE_ENV
// and loads config from config/default.json
// Access configuration (already loaded automatically)
console.log('Logging enabled:', propertiesmanager.responseinterceptor?.logging?.enabled);
// Configuration is automatically applied at startup
// You can override at runtime with configure()
configure({
logging: {
enabled: propertiesmanager.responseinterceptor?.logging?.enabled ?? true
},
errorHandling: {
rethrow: propertiesmanager.responseinterceptor?.errorHandling?.rethrow ?? false
}
});
// Get current configuration
const config = getConfig();
console.log('Current config:', config);
Run with different environments:
# Development (default)
node app.js
# Production
NODE_ENV=production node app.js
# Test
NODE_ENV=test node app.js
getConfig()Get the current configuration settings.
Returns: Object with current configuration
Example:
const { getConfig } = require('responseinterceptor');
const config = getConfig();
console.log('Logging enabled:', config.logging.enabled);
console.log('Error rethrow:', config.errorHandling.rethrow);
intercept(fn)The intercept(fn) function is a middleware that intercepts an Express response, so you can use it in the routes you want to intercept. This function returns an Express middleware used to intercept responses. It accepts a function fn as a parameter.
The function fn is defined as fn(content, contentType, request, callback) and is executed when the response is intercepted.
Parameters:
content - Contains the content of the intercepted responsecontentType - A string describing the content type (e.g., "application/json", "text/html", "text/css")request - An object containing the original Express req requestcallback - The callback function to call when your logic completes, with the new response content as a parameter: callback(newContent). The newContent can be a String, Object, HTML, text, etc.Example:
const express = require('express');
const app = express();
const responseinterceptor = require('responseinterceptor');
// Intercept all routes defined below
app.use(responseinterceptor.intercept(function(content, contentType, request, callback){
// Your custom logic here
// newResponse = ".... new Content ....";
callback(newResponse); // Return modified response
}));
interceptOnFly(req, res, fn)This function doesn't return an Express middleware and must be used internally in the endpoint logic to intercept responses conditionally. For example, if you need to intercept the response only when a particular condition in the endpoint logic is satisfied.
Parameters:
req - The original Express req request objectres - The original Express res response object to interceptfn - The interceptor function defined as fn(content, contentType, request, callback):
content - Contains the content of the intercepted responsecontentType - A string describing the content typerequest - The original Express req request objectcallback - The callback function: callback(newContent)Example:
const express = require('express');
const app = express();
const responseinterceptor = require('responseinterceptor');
app.get("/resource", function(req, res, next){
// Your logic to determine if interception is needed
const intercept = your_Logic ? true : false;
if(intercept){
responseinterceptor.interceptOnFly(req, res, function(content, contentType, request, callback){
// Your custom logic here
// newResponse = ".... new Content ....";
callback(newResponse); // Return modified response
})
}
res.send("Your Interceptable Content");
});
interceptByStatusCode(statusCodes, callback)Intercepts Express.js responses based on specific HTTP status codes, for example, to render a custom HTML page or JSON message instead of the default error output.
Description:
interceptByStatusCode(statusCodes, callback) temporarily overrides res.end() to detect when a response is about to be sent with a specific status code (e.g., 403, 404, 500). When a match occurs, it calls your callback, allowing you to customize the response before it's sent. A built-in anti-loop flag ensures the middleware doesn't re-trigger itself when the callback sends the new response.
Signature:
interceptByStatusCode(statusCodes, callback)
Parameters:
| Parameter | Type | Description |
|---|---|---|
statusCodes | number | number[] | A single status code or an array of status codes to intercept |
callback | (req, respond) => void | Function executed when one of the specified status codes is detected |
Callback Parameters:
req - The Express request objectrespond(newStatusCode, content, contentType?) - A helper function to send a new response
newStatusCode - Required HTTP status code (e.g., 200, 403, 404)content - Required HTML, string, object, or Buffer to send in the responsecontentType - Optional explicit Content-Type header (e.g., 'application/json; charset=utf-8', 'text/html; charset=utf-8')
application/json{ or [) → application/json<!DOCTYPE, <html>, <body>, <head>) → text/htmlapplication/octet-streamtext/plainExample:
const express = require('express');
const app = express();
const { interceptByStatusCode } = require('responseinterceptor');
// Example 1: Auto-detect Content-Type (HTML)
app.use(interceptByStatusCode(403, (req, respond) => {
respond(200, '<h1>Access Denied</h1><p>You are not authorized to view this page.</p>');
}));
// Example 2: Auto-detect Content-Type (JSON)
app.use(interceptByStatusCode(404, (req, respond) => {
respond(404, { error: 'Not Found', path: req.path, timestamp: Date.now() });
}));
// Example 3: Explicit Content-Type for custom formats
app.use(interceptByStatusCode(500, (req, respond) => {
const xmlError = '<error><code>500</code><message>Internal Server Error</message></error>';
respond(500, xmlError, 'application/xml; charset=utf-8');
}));
// Example route that triggers the interception
app.get('/private', (req, res) => {
res.status(403).send('Forbidden');
});
interceptByStatusCodeRedirectTo(statusCodes, callback)interceptByStatusCodeRedirectTo is an Express.js middleware that intercepts outgoing HTTP responses with specific status codes (e.g., 403, 404, 500) and automatically redirects the request to another route. It can be used to handle unauthorized, forbidden, or missing resources gracefully — for example, sending users to a custom "Access Denied" or "Not Found" page.
The middleware supports two usage modes:
interceptByStatusCodeRedirectTo(statusCodes, callback)
| Name | Type | Description |
|---|---|---|
statusCodes | number | number[] | A single status code (e.g., 403) or an array of codes (e.g., [403, 404]) to intercept |
callback | function | string | Determines how redirection occurs. • If a function, it will be called as callback(req, redirect) where redirect is a helper to trigger a redirect• If a string, it represents a static route path to redirect to directly |
Redirect users differently depending on the request or session:
const express = require('express');
const { interceptByStatusCodeRedirectTo } = require('responseinterceptor');
const app = express();
app.use(
interceptByStatusCodeRedirectTo(403, (req, redirect) => {
if (req.user) {
redirect('/no-access');
} else {
redirect('/login');
}
})
);
If a 403 response is about to be sent:
/no-access/loginRedirect all matching status codes to a fixed route:
const express = require('express');
const { interceptByStatusCodeRedirectTo } = require('responseinterceptor');
const app = express();
app.use(
interceptByStatusCodeRedirectTo([403, 404], '/error-page')
);
Any 403 or 404 response automatically redirects to /error-page.
res.end() temporarily to intercept the response before it is finalizedstatusCode matches one of the provided values:
callback (or redirect path) is executedres.redirect(newRoute)res.__interceptHandled) prevents recursive loops, ensuring that redirects do not trigger interception againres.end()403 Forbidden occurs404 Not Found503 Service Unavailable)Returns an Express middleware function (req, res, next).
Starting from version 2.0.6, interceptByStatusCode includes automatic Content-Type detection. When you don't specify a contentType parameter, the middleware intelligently detects the appropriate Content-Type based on the response content:
const { interceptByStatusCode } = require('responseinterceptor');
app.use(interceptByStatusCode(404, (req, respond) => {
// Automatic detection: application/json
respond(404, { error: 'Not Found', code: 404 });
// Automatic detection: text/html
respond(404, '<h1>Page Not Found</h1><p>The requested resource does not exist.</p>');
// Automatic detection: application/json (JSON string)
respond(404, '{"error": "Not Found"}');
// Automatic detection: text/plain
respond(404, 'Simple error message');
}));
Detection Rules:
| Content Type | Detection Logic |
|---|---|
application/json | JavaScript objects or strings starting with { or [ and ending with } or ] |
text/html | Strings containing <!DOCTYPE, <html>, <body>, or <head> tags |
application/octet-stream | Buffer objects |
text/plain | All other string content (fallback) |
For complete control over the Content-Type header, use the optional third parameter:
app.use(interceptByStatusCode(404, (req, respond) => {
// Explicit XML content type
respond(404, '<error><code>404</code><message>Not Found</message></error>',
'application/xml; charset=utf-8');
// Explicit custom content type
respond(404, customBinaryData, 'application/pdf');
// Explicit charset
respond(404, htmlContent, 'text/html; charset=iso-8859-1');
}));
The middleware now supports Buffer objects for binary content:
app.use(interceptByStatusCode(500, (req, respond) => {
const imageBuffer = fs.readFileSync('./error-image.png');
respond(500, imageBuffer, 'image/png');
}));
All interceptor functions now include comprehensive error handling:
TypeError exceptions// Parameter validation example
try {
app.use(interceptByStatusCode(null, callback)); // Throws TypeError
} catch (err) {
console.error(err.message); // "statusCodes must be a number or an array of numbers"
}
// Callback error handling
app.use(interceptByStatusCode(404, (req, respond) => {
throw new Error('Oops!'); // Error is caught, original response continues
}));
Console logging is automatically disabled in production environments. To enable logging in production, remove the NODE_ENV=production check:
// Development: logs are visible
// Production: logs are suppressed
// To force logging in production, set NODE_ENV differently:
// NODE_ENV=development node app.js
The responseinterceptor integrates seamlessly with propertiesmanager for centralized, environment-based configuration management.
Setup:
npm install propertiesmanager
config/default.json with environment-based configuration:{
"production": {
"responseinterceptor": {
"logging": { "enabled": false },
"errorHandling": { "rethrow": false }
}
},
"test": {
"responseinterceptor": {
"logging": { "enabled": false },
"errorHandling": { "rethrow": true }
}
},
"dev": {
"responseinterceptor": {
"logging": { "enabled": true },
"errorHandling": { "rethrow": false }
}
}
}
const propertiesmanager = require('propertiesmanager').conf;
const { configure } = require('responseinterceptor');
// PropertiesManager automatically loads config based on NODE_ENV
// Configuration is applied at startup automatically
// Optional: Override at runtime
configure({
logging: {
enabled: propertiesmanager.responseinterceptor?.logging?.enabled ?? true
},
errorHandling: {
rethrow: propertiesmanager.responseinterceptor?.errorHandling?.rethrow ?? false
}
});
Environment Selection:
# Development (default)
node app.js
# Production
NODE_ENV=production node app.js
# Test
NODE_ENV=test node app.js
Benefits:
configure()See examples/06-propertiesmanager-config.js for a complete working example.
You can chain multiple interceptByStatusCode middleware for different status codes:
// Handle 404 errors
app.use(interceptByStatusCode(404, (req, respond) => {
respond(404, { error: 'Page Not Found', path: req.path });
}));
// Handle 500 errors
app.use(interceptByStatusCode(500, (req, respond) => {
respond(500, { error: 'Internal Server Error', timestamp: Date.now() });
}));
// Handle multiple codes with one handler
app.use(interceptByStatusCode([401, 403], (req, respond) => {
respond(403, '<h1>Access Denied</h1>');
}));
Intercept a group of routes and add a timestamp field to the response when the body Content-Type is "application/json":
const express = require('express');
const router = express.Router();
const responseinterceptor = require('responseinterceptor');
// ############ Routes without interception ############
router.get("/", function(req, res, next){
res.status(200).send({"content": "myContent"});
});
// Additional routes without interception...
// ############ Routes with interception ############
router.use(responseinterceptor.intercept(function(body, bodyContentType, request, callback){
let newResponse = body;
if(bodyContentType === "application/json") // Check if response is JSON
newResponse.timestamp = Date.now(); // Add current timestamp
callback(newResponse); // Return modified response with timestamp
}));
router.get("/withTimestamp", function(req, res, next){
// This response WILL be intercepted and timestamp will be added
res.status(200).send({"content": "myContent"});
});
// Additional routes to intercept...
In app.js use the "/intercept" route:
const express = require('express');
const routeInt = require('./routes/routeInt');
const app = express();
app.use('/intercept', routeInt);
Call the service with curl to see the results:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/intercept
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 23
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"content":"myContent"} // Response without timestamp field
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/intercept/withTimestamp
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 51
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
{"content":"myContent","timestamp":"1478870174325"} // Response with timestamp field
Intercept a group of routes and replace all HTML <ul> tags with <ol> tags:
const express = require('express');
const router = express.Router();
const responseinterceptor = require('responseinterceptor');
const htmlContent = '<html>\
<head> </head>\
<body contenteditable="false">\
<h2>An unordered HTML list</h2>\
<ul>\
<li>Coffee</li>\
<li>Tea</li>\
<li>Milk</li>\
</ul>\
</body>\
</html>';
// ############ Routes without interception ############
router.get("/", function(req, res, next){
res.status(200).send(htmlContent);
});
// Additional routes without interception...
// ############ Routes with interception ############
router.use(responseinterceptor.intercept(function(body, bodyContentType, request, callback){
let newResponse = body;
if(bodyContentType === "text/html") // Check if response is HTML
newResponse = body.replace("<ul>", "<ol>").replace("</ul>", "</ol>"); // Replace ul tags with ol tags
callback(newResponse); // Return modified HTML
}));
router.get("/withOLTag", function(req, res, next){
// This response WILL be intercepted and <ul> tags will be replaced with <ol>
res.status(200).send(htmlContent);
});
// Additional routes to intercept...
In app.js use the "/intercept" route:
const express = require('express');
const routeInt = require('./routes/routeInt');
const app = express();
app.use('/intercept', routeInt);
Call the service with curl to see the results:
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/intercept
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 316
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
// Response with original <ul> tag
<html>
<head> </head>
<body contenteditable="false">
<h2>An unordered HTML list</h2>
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
</body>
</html>
$
$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://hostname/intercept/withOLTag
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 316
ETag: "35-6BXjKyRXlm+rSEU9a23z/g"
Date: Fri, 11 Nov 2016 13:16:44 GMT
Connection: keep-alive
// Response with modified <ol> tag
<html>
<head> </head>
<body contenteditable="false">
<h2>An unordered HTML list</h2>
<ol>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ol>
</body>
</html>
The package includes TypeScript definitions. Here's an example of using responseinterceptor with TypeScript:
import express, { Request, Response, NextFunction } from 'express';
import * as responseinterceptor from 'responseinterceptor';
const app = express();
// Type-safe interceptor
app.use(responseinterceptor.intercept((
body: any,
bodyContentType: string,
request: Request,
callback: (newContent: any) => void
) => {
let newResponse = body;
if (bodyContentType === "application/json") {
newResponse.timestamp = Date.now();
newResponse.server = "MyAPI";
}
callback(newResponse);
}));
// Status code interception with TypeScript
app.use(responseinterceptor.interceptByStatusCode(
[403, 404],
(req: Request, respond: (status: number, content: any, contentType?: string) => void) => {
// Auto-detect Content-Type (JSON)
respond(404, {
error: true,
message: "Resource not found or access denied"
});
}
));
// With explicit Content-Type
app.use(responseinterceptor.interceptByStatusCode(
500,
(req: Request, respond: (status: number, content: any, contentType?: string) => void) => {
respond(500, '<error>Internal Server Error</error>', 'application/xml; charset=utf-8');
}
));
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Logging is automatically disabled when process.env.NODE_ENV === 'production'. If you need to customize this behavior, you can set the environment variable:
export NODE_ENV=production
Yes! All callbacks support async functions:
app.use(interceptByStatusCode([404], async (req, res, newStatusCode, content) => {
const customContent = await fetchCustomErrorPage();
res.respond(newStatusCode, customContent);
}));
Use Buffer with explicit Content-Type:
app.use(intercept((body, req, res) => {
if (req.path === '/logo') {
const imageBuffer = fs.readFileSync('./logo.png');
return res.respond(200, imageBuffer, 'image/png');
}
return body;
}));
Errors are caught automatically and the original response is sent to prevent connection issues:
app.use(intercept((body, req, res) => {
try {
// Your logic
return transformedBody;
} catch (error) {
console.error('Intercept error:', error);
return body; // Fallback to original
}
}));
undefined or null as content?The middleware automatically converts undefined and null to empty strings to prevent crashes:
app.use(interceptByStatusCode([404], (req, respond) => {
// These are automatically handled:
respond(404, undefined); // Converted to ''
respond(404, null); // Converted to ''
// Warning is logged in development:
// [responseinterceptor] Warning: undefined content provided for GET /path (status 404)
}));
Best practice: Always provide explicit content to avoid warnings.
Yes, pass an array:
app.use(interceptByStatusCode([400, 401, 403, 404, 500], (req, res, statusCode, content) => {
res.respond(statusCode, customErrorPage(statusCode));
}));
Use req.get('content-type') or check the body:
app.use(intercept((body, req, res) => {
const contentType = res.get('Content-Type');
if (contentType && contentType.includes('application/json')) {
return { ...body, modified: true };
}
return body;
}));
Minimal! Benchmarks show:
See test/performance.test.js for detailed benchmarks.
The API is backward compatible. Key changes:
contentType parameter for explicit typesYes! Type definitions are included:
import { intercept, interceptByStatusCode } from 'responseinterceptor';
app.use(intercept((body: any, req: Request, res: Response) => {
// Your typed logic
return body;
}));
Install propertiesmanager and create config/default.json:
npm install propertiesmanager
{
"production": {
"responseinterceptor": {
"logging": { "enabled": false },
"errorHandling": { "rethrow": false }
}
},
"dev": {
"responseinterceptor": {
"logging": { "enabled": true },
"errorHandling": { "rethrow": false }
}
}
}
PropertiesManager automatically loads configuration based on NODE_ENV. See Integration with PropertiesManager for details.
MIT License
Copyright (c) 2016 aromanino
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Author:
CRS4 Microservice Core Team (cmc.smartenv@crs4.it)
Contributors:
FAQs
This package allows intercept an express response ( res.send , res.end , res.write ) and update or upgrade response with your other logic
We found that responseinterceptor demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.