Passing parameters to express middleware in routes - node.js

I am building a nodejs + express RESTful server, and am trying to leverage middleware to ease authorization of specific actions.
What I am trying to achieve is to pass parameters to my authorization middleware functions. I was wondering if it is at all possible to do this in the routes or if I have to extract the parameters in the middleware function. I was hoping to avoid that behavior as I have been *hum* not entirely consistent in my URL parameter names.
What I would like to do is something like this:
router.get(
'/:productId',
auth.can_get_this_product(productId), // Pass the productId here
controller.perform_action_that_requires_authorization
);
But this is not possible. Because I have other routes where the names of parameters might not be the same (ie: router.get(/:p_id, auth.can_get_thi...). I realize I should probably just go back and make sure that my parameter names are consistent everywhere and retrieve the parameters in the middleware using req.param('productId')but I am curious if it would be at all possible.

Well, I suppose you can pass the params hash key and then use that.
router.get(
'/:productId',
auth.can_get_this_product('productId'), // Pass the productId *KEY* here
controller.perform_action_that_requires_authorization
);
//....
function can_get_this_product(productIdKey) {
var productId = req.params[productIdKey];
//....
}
But of course, we both know you should just bite the bullet and refactor those names.

Related

Expressjs higher order router vs appending to request

Let's say I want to pass to an ExpressJS route callback an object.
I know I can append to app:
// router.js
const getFoo = (req, res) => res.json(req.app.foo);
// index.js
const app = express();
app.foo = {};
app.get('/foo', getFoo);
or I can use a higher order function:
// router.js
const getFoo = foo => (req, res) => res.json(foo);
// index.js
const app = express();
const foo = {};
app.get('/foo', getFoo(foo));
Both are easy to write, extend and test.
But, I don't know the implications of the solutions and whether one is better.
Is there anyone knowing real differences between the two approaches?
I think the second solution is more correct, here's why.
imagine you get used to the first solution and one day you need to send something called post or get or anything with the name of app property and you forget that there is already a property named like that, so you override original property without even realizing and when you call app.post() program will crash.
Believe me, you don't want hours of research wasted on something like that and realizing that you simply overrode original method
Also, in my opinion, it's always a bad idea mutating original object which wasn't generated by you
As #vahe-yavrumian mentioned it is not a good idea to mutate the state of the object created by a third party library.
between you can also use app.get() and app.set() methods to pass any data to the other routers in the queue (seems those methods are there just for this purpose.)
more information at https://expressjs.com/en/api.html.
The second solution easily allows you to pass different value for foo on different routes, if you ever found a need to do that.
The first solution essentially puts the value on the app singleton, which has all the implications of using singletons. (And as mentioned by #Anees, for express specifically the app settings with get and set are the proper place to store this, not a custom property)

Koa-router getting parsed params before hitting route

I'm using koa2 and koa-router together with sequelize on top. I want to be able to control user access based on their roles in the database, and it's been working somewhat so far. I made my own RBAC implementation, but I'm having some trouble.
I need to quit execution BEFORE any endpoint is hit if the user doesn't have access, considering endpoints can do any action (like inserting a new item etc.). This makes perfect sense, I realize I could potentially use transactions with Sequelize, but I find that would add more overhead and deadline is closing in.
My implementation so far looks somewhat like the following:
// initialize.js
initalizeRoutes()
initializeServerMiddleware()
Server middleware is registered after routes.
// function initializeRoutes
app.router = require('koa-router')
app.router.use('*', access_control(app))
require('./routes_init')
routes_init just runs a function which recursively parses a folder and imports all middleware definitions.
// function initializeServerMiddleware
// blah blah bunch of middleware
app.server.use(app.router.routes()).use(app.router.allowedMethods())
This is just regular koa-router.
However, the issue arises in access_control.
I have one file (access_control_definitions.js) where I specify named routes, their respective sequelize model name, and what rules exists for the route. (e.g. what role, if the owner is able to access their own resource...) I calculate whether the requester owns a resource by a route param (e.g. resource ID is ctx.params.id). However, in this implementation, params don't seem to be parsed. I don't think it's right that I have to manually parse the params before koa-router does it. Is anyone able to identify a better way based on this that would solve ctx.params not being filled with the actual named parameter?
edit: I also created a GitHub issue for this, considering it seems to me like there's some funny business going on.
So if you look at router.js
layerChain = matchedLayers.reduce(function(memo, layer) {
memo.push(function(ctx, next) {
ctx.captures = layer.captures(path, ctx.captures);
ctx.params = layer.params(path, ctx.captures, ctx.params);
ctx.routerName = layer.name;
return next();
});
return memo.concat(layer.stack);
}, []);
return compose(layerChain)(ctx, next);
What it does is that for every route function that you have, it add its own capturing layer to generate the params
Now this actually does make sense because you can have two middleware for same url with different parameters
router.use('/abc/:did', (ctx, next) => {
// ctx.router available
console.log('my request came here too', ctx.params.did)
if (next)
next();
});
router.get('/abc/:id', (ctx, next) => {
console.log('my request came here', ctx.params.id)
});
Now for the first handler a parameter id makes no sense and for the second one parameter did doesn't make any sense. Which means these parameters are specific to a handler and only make sense inside the handler. That is why it makes sense to not have the params that you expect to be there. I don't think it is a bug
And since you already found the workaround
const fromRouteId = pathToRegexp(ctx._matchedRoute).exec(ctx.captures[0])
You should use the same. Or a better one might be
var lastMatch = ctx.matched[ctx.matched.length-1];
params = lastMatch.params(ctx.originalUrl, lastMatch.captures(ctx.originalUrl), {})

Express 4 route handling with named parameters and router.param()

Is there a clean way to handle key/value query params in Express 4 routes?
router.route('/some/route?category=:myCategory')
I want to detect the presence of 'myCategory' in this route and use router.param([name], callback) to handle the associated logic.
router.param('myCategory', function(req, res, next, id) {
/* some logic here... */
});
The above 'router.param()' works fine if I have a route like /some/route/:myCategory but fails if I use
router.route('/some/route?category=:myCategory')
Am I doing something wrong here, or is this not supported in the Express 4 router out of the box?
Express treats properties after a ? as query params. So for:
/some/route?mycategory=mine
you would have to use:
req.query.mycategory or req.query['mycategory']
See this for more examples.

Overloading functions for an api in node - Best practice?

I'm currently building a small express powered node app to power a RESTful API exposing data from a node module I wrote. One of the functions in the module takes three arguments, but I want to allow the usage of the API by specifying just one, two, the other two or all three arguments.
So even starting to write the routes like this already feels ridiculous.
app.get('/api/monitor/:stop/:numresults', apiController.monitorNum);
app.get('/api/monitor/:stop/:timeoffset', apiController.monitorOff);
app.get('/api/monitor/:stop', apiController.monitor);
Especially since I don't know how to specify the difference between the first two, as numresults and timeoffset are both just integers.
What would a best practice in this situation look like?
The first problem you face is that you have identical routes, which are not possible if you're using express (I'm assuming that's what you're using). Instead you probably want one route and utilise the query object instead:
app.get('/api/monitor/:stop/', function (req, res, next) {
var stop = req.params.stop,
numResults = req.query.numResults,
timeOffset = req.query.timeOffset;
yourFunc(stop, numResults, timeOffset);
});
That way you can call the api with the following url: http://example.com/api/monitor/somethingAboutStop/?numResults=1&timeOffset=2. It looks like the stop parameter can also be moved to the query object but it's up to you.
You can use a catchall route then parse it yourself.
Example:
app.get('/api/monitor/*', apiController.monitor);
Then in apiController.monitor you can parse the url further:
exports.monitor = function(req, res) {
var parts = req.url.split('/');
console.log(parts); // [ '', 'api', 'monitor', '32', 'time' ]
console.log(parts.length); // 5
res.end();
};
So, hit the /api/monitor/32/time, and you get that array above. Hit it with /api/monitor/something/very/long/which/you/can/parse and you can see where each of your params go.
Or you can help yourself, like /api/monitor/page/32/offset/24/maxresults/14/limit/11/filter/by-user
Though, as Deif has told you already, you usually do pagination with query parameters, maxResults & page being your usual params.

Consolidating Routes in Express.js

I'm a novice programmer working on a web app. As I have things right now there is a route for every single query to my database. I know there must be a way to use route parameters to direct the route to executing the right function but I am having problems in implementation.
Here is what my routes look like right now:
var database = require('./routes/database');
app.get('/query/type', database.type);
app.get('/query/test', database.test);
app.get('/query/another', database.another);
app.get('/query/onemore', database.onemore);
Each route is mapped to a function in the database.js file. I would like to try to implement something in the following format which would handle the queries with a single line:
app.get('/query/:query', database.query)
Where it executes whichever function is named in the parameter :query.
Is there an easy way of implementing this?
you can create a function that will parse the parameter and use associative array to build the function you want to execute then invoke it. see code below.
function parseParam(req, res) {
var func = database[req.param('query')];
func(req, res);
}
app.get('/query/:query', parseParam);

Resources