Best example
Try this code to find out which arguments are passed to res.send :
const express = require('express'); const app = express(); var router = express.Router(); var port = process.env.PORT || 3000; router.get('/test', function(req, res) { console.log('ACTUAL RESPONSE'); res.send({ message: "testing" }); }); app.use(function(req, res, next){ console.log("INTERCEPT-REQUEST"); const orig_send = res.send; res.send = function(arg) { console.log("INTERCEPT-RESPONSE", JSON.stringify(arguments)); orig_send.call(res, arg); }; next(); }); app.use("/api", router); app.listen(process.env.PORT || 3000, function () { console.log("Running"); });
(I also changed the Startup print to print it when the server is actually listening - your && code was printed before the server was listening - but that is not so important here).
Now after launch:
curl http://localhost:3000/api/test
output to the server console:
Running INTERCEPT-REQUEST ACTUAL RESPONSE INTERCEPT-RESPONSE {"0":{"message":"testing"}} INTERCEPT-RESPONSE {"0":"{\"message\":\"testing\"}"}
What will happen
As you can see, your handler is actually called once by your code, and the object is called the first (and only) argument. But then it is again called an object serialized in JSON. This is how res.send works internally - see below for more details. Since you put your intercept function in a valid response object, I assume that it calls itself with a JSON argument, and it doesn't even know that it is calling your function in the meantime.
How to avoid it
Try using an object serialized in JSON yourself:
const express = require('express'); const app = express(); var router = express.Router(); var port = process.env.PORT || 3000; router.get('/test', function(req, res) { console.log('ACTUAL RESPONSE'); res.send(JSON.stringify({ message: "testing" })); }); app.use(function(req, res, next){ console.log("INTERCEPT-REQUEST"); const orig_send = res.send; res.send = function(arg) { console.log("INTERCEPT-RESPONSE", JSON.stringify(arguments)); orig_send.call(res, arg); }; next(); }); app.use("/api", router); app.listen(process.env.PORT || 3000, function () { console.log("Running"); });
Now it prints:
Running INTERCEPT-REQUEST ACTUAL RESPONSE INTERCEPT-RESPONSE {"0":"{\"message\":\"testing\"}"}
Call res.send only once.
Description
Now this is the code that processes the arguments of the res.json object:
if (chunk === null) { chunk = ''; } else if (Buffer.isBuffer(chunk)) { if (!this.get('Content-Type')) { this.type('bin'); } } else { return this.json(chunk); }
See: https://github.com/expressjs/express/blob/master/lib/response.js#L144-L154
You get the else branch, and it calls this.json() (which res.json() really is) with your argument.
But guess what - res.json() calls res.send() on this line:
return this.send(body);
See: https://github.com/expressjs/express/blob/master/lib/response.js#L250
Which call the interception function calls (the second time) before running real res.send() .
So the mystery is solved. :)