How should I pass three arguments to an express/node arrow function? - node.js

I am declaring my function like this:
const parseConnections = (connectionsCSVPath, req, res) => {
//do a bunch of stuff
}
Inside the function, if I try to call res.locals.something, I get an error saying "cannot read property locals of undefined" I have tried several other syntaxes, such as this:
const parseConnections = ((connectionsCSVPath, req, res) => {
//do a bunch of stuff
})
this:
const parseConnections = (connectionsCSVPath, (req, res) => {
//do a bunch of stuff
})
and this:
const parseConnections = connectionsCSVPath, (req, res) => {
//do a bunch of stuff
}
and they all throw errors. What is the proper way to pass these 3 arguments to the function so that all 3 are defined inside?
Edit*: The function is then called like this:
router.post(
'/upload/engagements/batch', checkIfAuthenticated,
parseConnections('./tmp/connections.csv'),
parseMessages('./tmp/messages.csv'), (req, res) => {
//do a bunch of stuff
}

The problem is not (necessarily) with how you define the function but with how you are using it.
parseConnections('./tmp/connections.csv') calls the function right then and there. You are only passing a single argument to it, so req and res will be undefined.
function foo(a, b, c) {
console.log('a:', a);
console.log('b:', b);
console.log('c:', c);
}
foo('first argument');
However, you cannot pass values for req and res because these values are created and passed by express itself.
Essentially you are making the mistake of calling a function where you should be passing it. router.post expects to be passed one or more functions. But you are calling parseConnections and pass its return value instead which is probably undefined.
Here is a simple example that demonstrates the difference:
function foo(x) {
console.log('inside foo', 'x is ', x);
}
// bar expects to be passed a function that it can call
function bar(callback) {
console.log('bar received:', callback);
try {
callback(42);
} catch(e) {
console.error(e);
}
}
// this will work as expected
console.log('Passing a function');
bar(foo);
// this will error because `bar` doesn't receive a function.
// this is what you are doing
console.log('Calling a function and passing its return value');
bar(foo(21));
One way to fix your problem is to make parseConnections return a function, which is then received by router.post. I'm using normal function declarations here so that the syntax is not too confusing:
function parseConnections(connectionsCSVPath) {
return function(req, res) {
//do a bunch of stuff
};
}
This requires no changes to your router.post call.
Another solution is to pass a function to router.post that calls parseConnections instead, passing along req and res:
router.post(
'/upload/engagements/batch',
checkIfAuthenticated,
(req, res) => parseConnections('./tmp/connections.csv', req, res),
// alternatively you can use `.bind`:
// parseConnections.bind(null, './tmp/connections.csv'),
parseMessages('./tmp/messages.csv'), // <- this is likely wrong as well,
// but I leave this to you to figure out
(req, res) => {
//do a bunch of stuff
}
);

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

Return res and next in a custom express function

How do I return both res and next in an express function:
const customfunction = async (req, res, next) => {
try {
// how do I set cookie and return next()?
return res.cookie('someToken', someToken, {
signed: true,
// etc...
}
);
return next();
} catch (err) {
// catch here, for example, return res.status(401).clearCookie...
}
}
An express request handler (like something you pass to app.get() or router.post() or something like that) does not pay any attention to the return value from that handler.
So, return inside such a handler is used only for flow control to stop further execution of the function.
In addition, your code has two return statements one after the other:
return res.cookie(...);
return next();
Which makes no sense because the return next() line of code will never be executed as the function has already returned on the line before.
If this is middleware and you intend for some other request handler to still have a chance to process this request, then you would want something like this:
const customfunction = async (req, res, next) => {
res.cookie('someToken', someToken);
next();
};
It doesn't appear that there's any reason for the try/catch because neither of these should throw an exception (assuming your code doesn't have a syntax error in it).
But, if you really wanted the try/catch, you could do this:
const customfunction = async (req, res, next) => {
try {
res.cookie('someToken', someToken);
next();
} catch(e) {
// make sure logs can see that this unexpected error is happening
console.log("customFunction error", e);
res.clearCookie('someToken');
res.status(500).send("Internal Error"); // probably want a more glamorous error page
}
};

How do I handle errors in a function called from a route handler in Express, NodeJS?

This may be extremely stupid, but I haven't found much about this because I don't understand how I should search this.
I have a route handler that may call different functions depending on some request parameters, and I would like to know what's the best way to deal with errors inside the functions in order to pass errors to the error handling middleware.
Consider something like this:
router.get('/error/:error_id', (req, res, next) => {
my_function();
}
function my_function(){
// do something async, like readfile
var f = fs.readFile("blablabla", function (err, data) {
// would want to deal with the error
});
}
If an error occurs during fs.readFile, how do I pass the error to next to forward it to the error middleware? The only solution is to pass the next param to the function function my_function(next){...}?
In case the function didn't call any async I/O operation, a simple try/catch in the route handler would be ok (i suppose), like this:
router.get('/error/:error_id', (req, res, next) => {
try{
my_function();
} catch(e){
next(e);
};
}
function my_function(){
// do stuff
var f = fs.readFileSync("blablabla"); // possibly throws an error
}
Hope I make some sense.
You are totally correct that you should pass the next callback to my_function since fs.readFile is asynchronous.
router.get('/error/:error_id', (req, res, next) => {
my_function(next);
}
function my_function(next) {
fs.readFile("blablabla", function (err, data) {
if (err) {
next(err);
} else {
// Process the data
// Don't forget to call `next` to send respond the client
}
});
}
By the way, you cannot do
var f = fs.readFile(...)
because fs.readFile is asynchronous. The data should be handled within the callback.

How to call app.post('/deletemsg') inside app.post('/deletemsg') in nodejs express

Anyone help me..I am new in nodejs ..how to call one route inside another route.doing some query in getmsg route and result of the first one calling text route deletemsg..
app.post('/getmsg', function (req, res) {
app.post('/deletemsg',{id:2});//need to call this
}
app.post('/deletemsg', function (req, res) { //do something
}
A simple solution, put a function in the route /deletemsg
app.post('/getmsg', function (req, res) {
getCalled(req,res,{id:2});//pass req, res and the object you want to send
}
app.post('/deletemsg',getCalled) // pass function here
function getCalled(req, res, obj) {
//do something
}
i think this is not a gud trick if you want to delete column with app.post
you need to send id with body object the get look like this
$http.post('/getmsg',{id:2})
.success(function(success){
//do someting with success response
})
.error(function(err){
//do somthing with err response
})
and server should be like this (as NoDownvotesPlz suggests):
app.post('/getmsg', function (req, res) {
var id = req.body.id;
getCalled(req,res,id);//pass req, res and the object you want to send
}
app.post('/deletemsg',getCalled) // pass function here
function getCalled(req, res, obj) {
//do something
}

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