website timeout after adding a .inv to req,session - node.js

I am trying to add a unique ID to my req.session but when the function executes if I want to go to another page it timeout due to infinite loading. Is there a way to do this correctly?
app.use(function (req, res, next) {
if (req.query.inv) {
sql.query(`SELECT * FROM inv WHERE inv='${req.query.inv}';`, (error, result) => {
if(error) console.log(error);
if(result.length < 1) {
req.session.inv= '';
next()
} else {
req.session.inv = `?inv=${req.query.inv}`;
console.log(req.session.inv);
next()
}
});
} else {
if(!req.session.inv) {
req.session.inv= '';
next()
}
}
});

You have a middleware which must call next() when complete so that the next middleware in the stack can be called. See Express's Using middleware documentation.
Take a look at your logic - if inv is not in your query string but does exist in your session, then next() is never called. This aligns with the issue you are having - you add inv to your session, and then on your next page load you will be forever stuck in your middleware. You would instead want logic like this:
app.use(function (req, res, next) {
if (req.query.inv) {
/* .... */
} else if (!req.session.inv) {
req.session.inv= '';
}
next(); // this makes sure that next() always gets called
});
You also have a glaring SQL Injection risk because you are taking a raw query string value and passing it directly into your query. What you want is a parameterized query - I linked to node-postgres documentation even though I'm not sure what database you are actually using. As of now your query is not safe.

Related

What is next() doing in this Express route and why is its order in the code important?

I'm getting confused with next(); I read through this post which essentially says it passes control to the next route. In my code below, having next(); where it is causes me to get "Cannot set headers after they are sent to the client". However, if I comment that out and then restore the else clause of my if statement, it functions correctly both when an incorrect empID is passed as well as when a correct one is. I'm hoping someone could explain what exactly is happening? Why does the position of the next() matter? It seems like it would be called either way?
I'm trying to do what is happening in this post which is add a value to, say req.user, but I haven't been able to get that to work at all so I'm trying the method I have here.
let checkEmp = (req, res, next) => {
db.get("select * from Employee where id = $id", {$id: req.empID},
(err, row) => {
if (err || row === undefined) {
res.status(404).send();
// } else {
// next();
}
});
next();
};
// get all timesheets
timesheetRouter.get("/", getParams, checkEmp, (req, res, next) => {
if (req.empID) {
db.all("select * from Timesheet where employee_id = $id", {$id: req.empID},
(err, rows) => {
if (err) {
next(err);
} else {
return res.status(200).send({timesheets: rows});
}
});
} else {
return res.status(404).send("Nothing to see here");
}
});
Looks like db.get() is probably asynchronous, so in the example as shown, next() will be called before db.get() finishes and it moves on to the next handler. Then, when the db.get() finishes, it tries to send a response, but the response has already been sent by the anonymous function in the main handler. By moving the next() inside of db.get(), you're essentially waiting for it to finish before moving on.

Pass parameter to middleware function in expressjs

I am trying to add cache functionality to the nodejs.
I want to have code like this,
app.get('/basement/:id', cache, (req,res) => {
client.set('basement' + req.params.id,'hello:'+req.params.id)
res.send('success from source');
});
function cache(req,res,next) {
console.log('Inside mycache:' + req.params.id);
client.get('basement' + req.params.id, function (error, result) {
if (error) {
console.log(error);
throw error;
} else {
if(result !== null && result !== '') {
console.log('IN Cache, fetching from cache and returning it');
console.log('Result:' + result);
res.send('success from cache');
} else {
console.log('Not in Cache, so trying to fetch from source ');;
next();
}
}
});
}
I want to apply a middleware function named cache to the request received by the /basement/:id route.
The cache function will receive the key as its parameter.
Inside the cache I want to check for existence of cache and if so return it from there, otherwise I want to call the actual route handler. The key is based on one or more request parameters.
In this way, i will end up writing a separate cache function for every handler in my app.
The logic inside my cache function is generic expect for the key, which is based on the actual request object and may differ from method to method.
So, I want to have a generic cache function which can take the key as a parameter, so that I can have code like this,
app.get('/basement/:id', cache, (req,res) => {
client.set('basement' + req.params.id,'hello:'+req.params.id)
res.send('sucess from source');
});
I mean i will pass the cache key to the cache function. So, the cache function can be generic.
But, if I change my cache function like below as so as to receive the cache key, it does not work.
function cache(cachekey,req,res,next) {
}
Seems like I cannot have another parameter in my cache function to receive the passed parameter.
I would like to pass the cache key as a parameter to the function.
If someone has faced a similar issue, can you please help me on this.
But, if I change my cache function like below as so as to receive the
cache key, it does not work.
You can't because it's not a valid express middleware (It's actually an error middleware), express will pass: req, res, next in that order. and err, req, res, next for error middlewares.
Your cache function will need to return a middleware instead, so you can pass a key to it.
I wanted to create a generic cache function which can take any cache
key. In this case it was id, but other cases may have different keys
function cache(key, prefix = '') {
// Or arrange the parameters as you wish to suits your needs
// The important thing here is to return an express middleware
const cacheKey = prefix + req.params[key];
return (req, res, next) => {
console.log('Inside mycache:' + cacheKey);
client.get(cacheKey , function(error, result) {
if (error) {
console.log(error);
return next(error); // Must be an error object
}
if (result !== null && result !== '') {
console.log('IN Cache, fetching from cache and returning it');
console.log('Result:' + result);
return res.send('success from cache');
}
console.log('Not in Cache, so trying to fetch from source ');;
next();
});
}
}
And now you can use it like this:
app.get('/basement/:id', cache('id', 'basement'), (req, res) => { /* ... */ });
app.get('/other/:foo', cache('foo', 'other'), (req, res) => { /* ... */ });

How to use app.method() to do authorization

I am trying to use express then next('route') but I am not finding much in the docs on how to do this, it is mentioned here. But it doesn't explain how to do the
will work only in middleware functions that were loaded by using the app.METHOD() or router.METHOD() functions.
I don't get what they mean by that because I have never used app.METHOD() before and again am unable to find docs on how to do it properly with the next('route'). Would this work?
app.method('*', (req, res, next) => {
if(req.user == null) {
next();
} else {
User.findOne({"_id": req.user.id}, function(err, result) {
if(err){
console.log(err);
} else {
if(stripe.active == false && stripe.trial == false){
res.render('/dashboard/billing');
next('route');
} else {
next();
}
}
});
}
});
Would that even work? I am guessing somewhere in there I messed up something. If I did that right then it would find a user, then check is both is_active and is_trial are false and if so skip onto the next route. The idea of me doing this is so that a use can any part of the site that doesn't have a logged in user then when there is a logged in user and both are false I only let them go to /dashboard/billing. This was suggested to me as a way to prevent users who have not paid yet their trial is over from accessing my application.
Route I am testing on:
// dashboard
app.get('/dashboard',
setRender('dashboard/index'),
setRedirect({auth: '/login'}),
isAuthenticated,
(req, res, next) => {
},
dashboard.getDefault);
First of all, change the method to the right HTTP method request. In the documentation, it specifies what .METHOD() represents:
app.METHOD() functions, where METHOD is the HTTP method of the request
that the middleware function handles (such as GET, PUT, or POST) in
lowercase.
In other words, .METHOD() is a place holder for .get, .post, .put, etc.
Try changing that in your app:
app.get('*', (req, res, next) => {
if(req.user == null) {
next();
} else {
User.findOne({"_id": req.user.id}, function(err, result) {
if(err){
console.log(err);
} else {
if(stripe.active == false && stripe.trial == false){
res.render('/dashboard/billing');
next('route');
} else {
next();
}
}
});
}
});
See if you can work it out from there.
Update
Calling .next('route') right after res.render() cancels res.render() because .next('route') will send the request to the next router.

Express Router.use in middleware

I'm new to Express.
I am trying to route a request according to a value in DB. To do this I am calling a service function.
Routing is working successfully but I am loosing the request object.
I have tried to pass the req to the service but it didn't work.
Any help showing how to do this would be nice.
Here is my code block.
var companyService = require("services/companyService");
router.use('/', function (req, res, next) {
companyService.isCompanyOnline(req.body.companyCode).then(function (company) {
if (company) {
router.use('/', require("api/controllers/online"));
}
else {
router.use('/', require("api/controllers/offline"));
}
});
next();
});
module.exports = router;
Services.companyService:
function isCompanyOnline(code) {
var deferred = Q.defer();
companies.findOne({ companyCode: code }, function (err, company) {
if (err) deferred.reject(err.name + ': ' + err.message);
//if (err) throw err;
if (company) {
// return company online parameter
deferred.resolve(company.isOnline);
} else {
// company not found
deferred.resolve();
}
});
return deferred.promise;
}
You are losing the request object because you are not passing it anywhere.
I believe your main issue here is the fact that you have three route handlers registering on the same path /. If I am not mistaken they will be all called in-order that you add them. But the order in your depends on the if you are placing the router.use() calls. So it's unpredictable, and likely not going to work as you expect. Once they are registered they stay in the middleware stack until you restart the app.
I suggest you refactor to have the online/offline logic in one place, or register all your middlewares at once, so you know in which order they get called, and use next() accordingly.
On another note, if you want to pass an argument to required modules, do this:
Change api/controllers/online and the other, to accept an argument, and return the handler function that you are passing.
// your code in api/controllers/online and offline
module.exports = function (req) {
// now you have req available in-scope here
function yourFunctionThatNeedsReq (req) {
// do stuff with req
}
return yourFunctionThatNeedsReq;
};
Then update your require like so. Note the …(req).
router.use('/', require("api/controllers/online")(req) );
My solution:
First, define routes:
app.use('/api/online', require("api/controllers/online"));
app.use('/api/offline', require("api/controllers/offline"));
Then change the url in the router :
if (req.body.companyInfo.isOnline) {
req.url = '/online' + req.url + '/' + req.body.companyInfo.companyPath;
next();
}
else {
req.url = '/offline' + req.url + '/' + req.body.companyInfo.companyPath;
next();
}

Grouping and Managing node.js Middleware

If I've made some middleware that works together, what's the best convention for grouping and managing the functionality?
In my server.js file, I've currently just got them listed one after another with app.use calls.
It's occurred to me however that if the first one in my set doesn't produce any data, the subsequent ones in the group are fine to skip. I guess this is ultimately an aggregation although I haven't seen any examples of such in other projects.
The connect middleware has a good example for this kind of problem. Take a look at the bodyParser:
app.use(connect.bodyParser()); // use your own grouping here
is equivalent to
app.use(connect.json());
app.use(connect.urlencoded());
app.use(connect.multipart());
Internally the bodyParser function just passes the req and res objects through each of the before mentioned middleware functions
exports = module.exports = function bodyParser(options){
var _urlencoded = urlencoded(options)
, _multipart = multipart(options)
, _json = json(options);
return function bodyParser(req, res, next) {
_json(req, res, function(err){
if (err) return next(err);
_urlencoded(req, res, function(err){
if (err) return next(err);
_multipart(req, res, next);
});
});
}
};
The full code can be found at the github repo
edit
Informed in the comments bellow that passing an array will acieve the exact same thing, so no need for an additional module. :-)
I was looking for a way to do this too as my application is very granular, but I didn't want to nest everything as in the other answer.
I'm sure there is something more comprehensive out there already, but I did this in the end:
/**
* Macro method to group together middleware.
*/
function macro (...middlewares) {
// list of middlewares is passed in and a new one is returned
return (req, res, next) => {
// express objects are locked in this scope and then
// _innerMacro calls itself with access to them
let index = 0;
(function _innerMacro() {
// methods are called in order and passes itself in as next
if(index < middlewares.length){
middlewares[index++](req, res, _innerMacro)
} else {
// finally, next is called
next();
}
})();
}
}
And then use it like this:
var macro = macro(
middleware1,
middleware2,
middleware3
);
app.post('/rout', macro);

Resources