Pass parameter to middleware function in expressjs - node.js

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) => { /* ... */ });

Related

website timeout after adding a .inv to req,session

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.

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.

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();
}

Sails blueprints lifecycle

I need to add some additional data to result of find blueprint. I found this solution:
module.exports = {
find: function(req, res) {
return sails.hooks.blueprints.middleware.find(req, res);
}
}
but I can`t find any way to change response here, or add callback into the blueprint. I even try to change blueprint and add the cb in it:
module.exports = function findRecords (req, res, cb) {
...
if (typeof cb === 'function') res.ok(cb(result));
else res.ok(result);
but in this case it returns 500 statusCode every time (but with corresponding data)
I have been struggling with the same issue for a couple of time. Here is my hack (with explanation) to solve this.
The build in blueprint will always make a call to res.ok, res.notFound, or res.serverError if an error occurs. With altering of this method calls, it is possible to modify the output.
/**
* Lets expose our own variant of `find` in one of my controllers
* (Code below has been inserted into each controller where this behaviour is needed..)
*/
module.exports.find = function (req, res) {
const override = {};
override.serverError = res.serverError;
override.notFound = res.notFound;
override.ok = function (data) {
console.log('overriding default sails.ok() response.');
console.log('Here is our data', data);
if (Array.isArray(data)) {
// Normally an array is fetched from the blueprint routes
async.map(data, function(record, cb){
// do whatever you would like to each record
record.foo = 'bar';
return cb(null, record);
}, function(err, result){
if (err) return res.error(err);
return res.ok(result);
});
}
else if (data){
// blueprint `find/:id` will only return one record (not an array)
data.foo = 'bar';
return res.ok(data);
}
else {
// Oh no - no results!
return res.notFound();
}
};
return sails.hooks.blueprints.middleware.find(req, override);
};
Seems like only copy-paste solution existed. So I copy all code from files in node_modules/sails/lib/hooks/blueprints/actions to the actions of every controller and then change it.

Resources