Express.js - Get the res.send value - node.js

My question is exactly this one. The problem is that when I apply the solution to that question, nothing happens - the value of res.send isn't logged. I use express 4.16.4.
Here's my code:
/* FILE: /app.js: */
...
const postRoute = require('./middleware/postRoute');
const myRoute = require('./routes/myRoute');
...
app.use('/', myRoute);
app.use(postRoute);
...
/* FILE: /routes/myRoute */
...
router.post('/myRoute', [], (req, res, next) => {
res.send({ status:'success', message:'Test success. Feels good, man.' });
next();
});
...
/* FILE: /middleware/postRoute */
const postRoute = function(req, res, next) {
console.log('postRoute: ');
var send = res.send;
res.send = function(chunk, encoding){
res.send = send;
if (chunk) {
console.log('postRoute chunk: ', chunk); // This statement is never reached
}
res.send(chunk, encoding);
};
next();
};
module.exports = { postRoute };
When I make a POST request to /myRoute using Postman, it logs the following: postRoute:, and that's it. The second console.log() is never reached and presumably neither is any other statement in the same function() as the console.log().
The question I link to at the start of my question was from 2015, when I assume they used a different version of Express.js, and that's why their solution isn't working for me? In my middleware code example, I used the code from a question that was linked in one of that question's answers, because it's solution didn't work for me. But that solution's obviously also not working, otherwise I wouldn't be here - and it's from 2012! Who even knows what version of Express they used back then!?
So to reiterate and conclude my question: How do I use middleware to log the value passed to res.send()?
Follow up question (but let me know if I should rather ask this in a separate question): Is there a way to call middleware after the route/response from the route instead of globally. So router.post('/myRoute', [postRoute], (req, res, next) => {...}); instead of app.use(postRoute)?

Here's what I ended up doing. I kept my file structure exactly the same, but instead of res.send()ing from the route, I attached the object that I would have sent, to the res object and then call next() at the end of my route. So for instance, res.send({message:'blah'}); becomes res.return = {message:'blah'}; next();. I use .return because I don't believe such a property exists on the res object by default and I find it descriptive enough for my purposes. Then in my postRoute middleware, I can easily access res.return, after which I call return res.send(res.return).
Using my example from the question (app.js stays exactly as you see it):
/* FILE: /routes/myRoute */
...
router.post('/myRoute', [], (req, res, next) => {
if(theConditionIsMet()) {
res.return = { message:'The condition was met.' };
} else {
res.return = { message:'The condition was not met.' };
}
next();
});
....
router.post('/anotherRoute', [], (req, res, next) => {
// In this route I don't need to intercept the response, so I just do it like normal.
return res.send({ message:'Feels good, man.' });
});
...
/* FILE: /middleware/postRoute */
const postRoute = function(req, res, next) {
if(res.hasOwnProperty('return') {
return res.send(res.return);
}
};
module.exports = { postRoute };
One flaw I can already see is that I'll now have to restructure all my routes to use this method of res.send()ing (or at least all of the routes for which I want to intercept the return value). Because of this, and because I imagine someone more knowledgeable than me could probably figure out a better way to do it, I'm not going to accept my own answer.

Related

Express middleware with async operation that happens only once

I am trying to append some data to my request object using a middleware, but I want to do it only once the server is up.
So I tried doing it with a middleware, while trying to use a function's context, but it's a bit problematic to perform such an action on a middleware, because I cannot pass a promise as a middleware.
This is what I'm trying to do:
const setupData = async () => {
const data = await getSomeData();
return (req, res, next) => {
req.data = data;
next();
}
}
app.use(setupData());
I tried using the solution suggested here, but it won't work as this will happen on every request.
Any idea how can I go around this? I can always put the info on a global var, but I would like to avoid it.
I also saw some in-memory packages to help with it (such as node-cache), but I would like to do it with a middleware.
Thanks in advance
Just cache the result using a normal variable:
let data = null;
function setupData (req, res, next) {
if (data !== null) {
req.data = data;
next();
}
else {
getSomeData().then(result => {
data = result
req.data = data;
next();
});
}
}
app.use(setupData);
This is the minimal, least complicated implementation. You can of course refactor it to be much DRYer and less prone to mistakes by taking out the caching logic:
Cleaner Implementation
let cache = null;
async function getCachedData() {
if (cache === null) {
cache = await getSomeData();
}
return cache;
}
Which makes setupData much cleaner:
function setupData (req, res, next) {
getCachedData().then(data => {
req.data = data;
next();
});
}
Either way, the cache is triggered on the first request. This means that there is a possibility that a second request may arrive before the data is possibly cached. So at startup the getSomeData() function may run more than once.
Really call getSomeData() ONLY ONCE
If you really want to call getSomeData only once you must call it before setting up Express:
async function main () {
const data = await getSomeData();
const app = express();
//
// set up express middlewares...
//
app.use((req,res,next) => {
req.data = data;
next();
});
//
// set up routes...
//
app.listen(config.port);
}
main(); // start everything
The key here is to realize that we have been trying to do everything backwards: to set up a constant value asynchronously AFTER starting to set up Express. The natural flow of the program wants the constant value to exist BEFORE we begin setting up Express so we only perform everything else inside the async function (here called main). Not try to run the async function while setting up Express.
You can do it without async:-
const setupData = (req, res, next) => {
// You can put a condition here so that it runs only once
getSomeData().then((data) => {
req.app.locals.data = data //The data can be accessed in the next middleware using req.app.locals
next();
}).catch((error) => {
console.log("Error Occured");
res.status(400).end("Error Occurred");
})
}
app.use(setupData);
You should see the documentation of getSomeData and see how it works

Error [ERR_HTTP_HEADERS_SENT]: Cannot remove headers after they are sent to the client

I am performing a simple API fetch but I am receiving the following error:
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot remove headers after they are sent to the client and I am not sure why that is happening:
Code is below and also a print screen of the error is provided. I am not sure if that could be a problem related to the asynchronous behavior of nodejs:
var express = require('express');
var router = express.Router();
var axios = require('axios');
const NodeCache = require('node-cache');
const myCache = new NodeCache();
let hitCount = 0;
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/hello', async function(req, res, next) {
const allData = myCache.get('allData');
if (!allData) {
hitCount++;
console.log(`hit ${hitCount} number of times`);
try {
const { data } = await axios.get(
'https://api.vesselfinder.com/vesselslist?userkey=KEY'
);
const [ metaData, ships ] = data;
myCache.set('allData', data, 70);
console.log(data + 'This is the data');
res.send(data);
} catch (error) {
res.send(error);
console.log(error);
}
}
console.log('this is the data:', allData);
res.send(allData);
});
module.exports = router;
What I have done so far to solve this error was:
I used this source which has a wide explanation of the what the problem could be with a broad explanation of the errors type. However it was not useful to understand what was going on.
Also I found this useful and also this but this last one in particular was an error in the code for a missing statement return. My error seems to be related to a TypeError: data is not iterable but it is not possible because my subscription with the provided is perfectly working.
The closest post I found is this one which basically states that
"Sometimes it happens due to asynchronous behavior of nodejs." Now I see this happened to me but I am not sure how this could be solved in this situation.
A log of the error is also shown here in the print screen below for completeness:
What can I do to take care of this error? I did a lot of research and I am not sure how to take care of the asynchronous behavior of nodejs.
Thank for pointing to the right direction for solving this problem.
You get this error because you are calling res.send() multiple times:
router.get('/hello', async function(req, res, next) {
const allData = myCache.get('allData');
if (!allData) {
hitCount++;
console.log(`hit ${hitCount} number of times`);
try {
const { data } = await axios.get(
'https://api.vesselfinder.com/vesselslist?userkey=KEY'
);
const [ metaData, ships ] = data;
myCache.set('allData', data, 70);
console.log(data + 'This is the data');
res.send(data); // <-- FIRST TIME
} catch (error) {
res.send(error);
console.log(error);
}
}
console.log('this is the data:', allData);
res.send(allData); // <-- SECOND TIME
});
Calling res.send() will end the response so you cannot call it again.
You can wrap the second call inside an else condition to ensure that only one is invoked:
router.get('/hello', async function(req, res, next) {
const allData = myCache.get('allData');
if (!allData) {
// ...
}
else {
console.log('this is the data:', allData);
res.send(allData); // <-- SECOND TIME
}
});

Jest Express testing middleware with arguments

I'm pretty new to node and this is my first time unit testing an app. I'm doing well with Jest faking the request with Jest function as below
// Create a fake request
const mockRequest = (sessionData, body) => ({
session: { data: sessionData },
body
});
// Create a fake response
const mockResponse = () => {
const res = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
return res;
};
const mockNext = () => {
const next = jest.fn();
return next;
};
So I can use them like follows
doSomething(req, res, next);
expect(res.status).toHaveBeenCalledWith(201);
//or
expect(next).toHaveBeenCalled();
That's enough for all the cases until I found that my authorisation middleware includes a couple of parameters so I can not pass the fake res and req as below
exports.isAllowedTo = (par1, par2) => {
return async (req, res, next) => {
try {
//
// Grant logic here that needs par1 and par2
//
if(granted)
next();
else
return res.status(401).json({
error: "You don't have enough permission to perform this action"
});
} catch (err) {
res.status(406).json({
error: err.toString(),
})
}
}
}
If I test isAllowTo(req, res, next) with the mock req, res and next then I'm missing the 2 parameters needed by the function. Actually when I do this, the function isAllowTo() is not even called. I don't know how to deal with that. Any suggestion or approach?
Two months later I realized that the real problem is that I'm testing a function inside of another function.
So firstly I store the function in a variable so I can test it as a regular middleware.
test('Grant access if user role is allowed to', async () => {
const isAllowToTester = userController.isAllowedTo(par1, par2);
await isAllowToTester(req, res, next)
expect(next).toHaveBeenCalled();
});
Hope this helps someone else.
Credits to this post
Check out https://github.com/nock/nock it's a library dedicated to mocking requests and responses, it's really easy to use with unit tests/jest. I personally don't think is worth it to write your own mocking implementation.

Skipping a middleware in express

I'm not looking for authorization of a particular request and so I did this.If the request path matches I want to skip the auth.verifyToken middleware.How can I do this.I tried using return next() but its not working.
eventRouter.param('pin', (req, res, next, pin) => {
let path = `/event/matchpin/${pin}`;
if(req.path === path){
//do something here so that directly executes the route
}
//else it executes the auth.verifyToken middleware
next();
});
app.use('/user',auth.verifyToken,eventRouter);
next() is used to skip the middleware, you are just using it at the wrong place.
Try this code:
eventRouter.param('pin', (req, res, next, pin) => {
let path = `/event/matchpin/${pin}`;
if(req.path === path){
// Next will by pass this middleware
next();
}
//else it executes the auth.verifyToken middleware
});
app.use('/user',auth.verifyToken,eventRouter);

Is it possible to skip route middleware in node express?

Take the following POST function in express. (I am using express 3.5.1)
app.post('/example', someFunctionOne, someFunctionTwo, function(req, res){
if(!req.someVar){
return res.send(400, { message: 'error'});
} else{
return res.json(200, { message: 'ok'});
}
});
If I get some result from someFunctionOne which means someFunctionTwo is redundant, is there a way to skip someFunctionTwo and go to the last unnamed function which will send the response?
So I guess in the same way there is the "next()" function where is the "last()" function? If this is not possible why not? It seems like an oversight to me but is there a good reason?
You can do next('route') which will go to the next route entirely. This is not exactly what you need in this case, but it would work if you broke your code up into 2 separate routes.
However, I think there are generally 2 approaches to this kind of conditional logic:
make someFunctionOne put some state on the req instance when it gets the special result and make someFunctionTwo smart enough to check for that and when found call next() and bypass itself. This is the most idiomatic express thing to do, and it's how most middleware detect when they have been called more than once on the same request and avoid redoing their work again.
In someFunctionOne, when the special case happens, just invoke lastFunction directly. Remember the middleware abstraction isn't the holy grail. If your middleware are so tightly coupled, maybe they should be one middleware and some helper functions. There are lots of other ways to organize code that might feel more natural.
My instinct is to do something like this:
const funcOne = (req, res, next) => {
// do something
if (/* you were successful */) {
res.locals.shouldSkipFuncTwo = true
}
next()
}
const funcTwo = (req, res, next) => {
if (res.locals.shouldSkipFuncTwo) return next()
// do whatever stuff
next()
}
router.get('/', funcOne, funcTwo, (req, res) => {
res.status(200).send('hello world')
)}
If you haven't used res.locals before, here are the express docs on it. Basically, it's a property in the response object that's there for you to use as a container.
Probably the best way to do it to make some helper or put your own middleware into chain instead of your functions.
So your code will look like:
app.post('/example', oneOf(key, someFunctionOne, someFunctionTwo), function(req, res){
if(!req[key]){
return res.send(400, { message: 'error'});
} else{
return res.json(200, { message: 'ok'});
}
});
And the helper should be something like that:
function oneOf (key) {
var fns = Array.prototype.slice.call(arguments, 1);
var l = fns.length;
return function (req, res, next) {
var i = 0;
function _next () {
if (req[key] || i === l) return next();
fns[i](req, res, _next);
i += 1;
}
_next();
}
}
If you will decide to do it just here the code will looks like:
app.post('/example', functionOneOrTwo, function(req, res){
if(!req.someVar){
return res.send(400, { message: 'error'});
} else{
return res.json(200, { message: 'ok'});
}
});
function functionOneOrTwo(req, res, next) {
someFunctionOne(req, res, function () {
if (req.someVar) return next();
someFunctionTwo(req, res, next);
});
}
Simple, but untested ;-)
Actually I was facing the very same problem. And I just found the express-unless module which does exactly this: https://github.com/jfromaniello/express-unless

Resources