restify regular expression on named parameter - restify

I am using the restify 2.8.4.
Understood that named parameter with regular expression is not supported in
Mixing regex and :params in route #247
Instead of cobble both logics into one block.
server.get ('/user/:id', function (req, res, next) {
var id = req.params.id;
// check with isNaN()
// if string do this
// if number do that
}
I prefer below code structure:
//hit this route when named param is a number
server.get (/user\/:id(\\d+)/, function (req, res, next) {
var id = req.params.id;
// do stuff with id
}
//hit this route when named param is a string
server.get (/user\/:name[A-Za-z]+/, function (req, res, next) {
var name = req.params.name;
// do stuff with name
}
Is there a way I can split them into two separate concerns?

It looks like you have mostly already done it yourself. Just remove the identifiers from your route and make sure you use regex capture groups.
//hit this route when named param is a number
server.get (/user\/(\d+)/, function (req, res, next) {
var id = req.params[0];
// do stuff with id
}
//hit this route when named param is a string
server.get (/user\/([A-Za-z]+)/, function (req, res, next) {
var name = req.params[0];
// do stuff with name
}
Answer edited to incorporate Cheng Ping Onn's input.

Related

How to pass arguments to a callback function in express?

I'm using express middleware with NodeJS. I organized my routes with no callbacks, but for some of them it's better to mutualize functions, so I began to write some callbacks.
I have for example this function (I don't put the whole code, it's useless for my question here):
async function verification(req, res, next) {
And I use it like this in the route part:
router.post('/item/:id/dosomething',
verification,
async (req, res) => {
Everything works well, but if I want to continue using callbacks (sometimes it's a good solution to have a clean and efficient code), I'll have to pass some arguments to the function. Of course I tried this, and it didn't work:
router.post('/item/:id/dosomething',
verification(arg1, arg2),
async (req, res) => {
I searched on StackOverflow answers for "pass arguments to a callback function" but the few that could be interesting talked about a wrapping function, which I don't think I can implement in my case.
If anybody could give me a hand, that would be great. Thanks :)
Here is a snippet of how a next worked without writing it in the call (look at the verif):
The callback:
async function verif(req, res, next) {
let rows;
const {
id
} = req.params;
({ rows } = await db.query(`XXX`))
if (rows.length === 1) {
return next();
} else {
retour(req, res, 500, "No can do.");
}
Where it's called:
router.post('/mymusic/:id/addElements',
droits.verifRightsCB.bind(undefined, 'music', 'addInMyMusic'),
verif,
async (req, res) => {...
The last part of the route (async (req, res) =>) is only executed when the next() condition is fulfilled in the verif, although I didn't pass any argument.
function verification(arg1, arg2, req, res, next) {...}
router.post('/item/:id/dosomething',
verification.bind(undefined, arg1, arg2),
(req, res) => {...}
(The functions need not be async.)
Note that the router.post statement is executed during server start-up, not per request. Therefore you cannot write something like
router.post('/item/:id/dosomething',
verification.bind(undefined, arg1, req.params.arg),
(req, res) => {...}
because there is no req during server start-up. Instead, you may write
router.post('/item/:id/dosomething',
(req, res, next) => verification(arg1, req.params.arg, req, res, next),
(req, res) => {...}
The verification function can then
call next() after successful verification to invoke the other middleware function ((req, res) => {...})
or call next(err) after a verification error in order to skip the other middleware function and report the error.

app.get with multiple arguments - e.g. app.get('/', requireAuth, (req, res) => { })

I am wondering how express.js works when you specify multiple argument in the app.get, for example:
app.get('/', requireAuth, (req, res) => { })
Here, I believe you have the slash "/" as the route, and (req, res) as arguments for the callback function. But there is also the 'requireAuth' part which was used for authorization. But in the documentation I don't find anything about multiple arguments in the way we use 'requireAuth' now. It does mention that you can use an array of functions but that is not used here (https://expressjs.com/en/guide/routing.html). How does it work?
P.S. The 'requireAuth' code for reference:
module.exports = (req, res, next) => {
const { authorization } = req.headers;
// authorization === 'Bearer laksjdflaksdjasdfklj'
if (!authorization) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const token = authorization.replace('Bearer ', '');
jwt.verify(token, 'MY_SECRET_KEY', async (err, payload) => {
if (err) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const { userId } = payload;
const user = await User.findById(userId);
req.user = user;
next();
});
};
All the arguments are simply middle-ware functions that you call before the actual route logic is run(for individual routes). As long as there is next handling in each of the middle-ware functions, the chain will run till your route logic, and then exit where next isn't handled.
This is the magic of express. You can do many things with it, like error handling, handling different content types etc.

How does the return of control flow work in a chain of Express middleware functions?

I actually have a couple of questions about Express middleware chaining and thought I'd group them in this single post since they're closely related.
Q1: In a middleware function, if I send() the Response, pass to the next function, or just return, does any of the remaining code get executed?
router.get('/a', (req, res, next) => {
res.sendStatus(200);
// Does this ever get executed?
});
router.get('/b', (req, res, next) => {
next();
// Does this ever get executed?
});
router.get('/c', (req, res, next) => {
return;
// Does this stop the middleware chain? What happens to the request/response?
});
Q2: Can you pass on to another middleware function from inside of the preceding one? My reason for this is that I have to supply an argument to create a certain middleware, and that argument is dependent on which Express app object is the owner of the Request. So I need to access a property of the Request before it is passed to the middleware that needs to be created using that property. Would this work: ?
router.get('/d', (req, res, next) => {
var middlewareFunction = createDynamicMiddleware();
middlewareFunction(req, res, next); // Will this still work as expected?
});
Q3: How are the Request/Response arguments passed to the next middleware when you call next() without any arguments?
Q1: In a middleware function, if I send() the Response, pass to the next function, or just return, does any of the remaining code get
executed?
router.get('/a', (req, res, next) => {
res.sendStatus(200);
// Does this ever get executed?
// Yes, but don't call next() or res.send() or it will throw an error
});
router.get('/b', (req, res, next) => {
next();
// Does this ever get executed?
// Yes, but don't call next() or res.send() or it will throw an error
});
router.get('/c', (req, res, next) => {
return;
// Does this stop the middleware chain? What happens to the request/response?
// The request get stuck and after a while it will fail due 'timeout'
});
Q2: Can you pass on to another middleware function from inside of the preceding one? My reason for this is that I have to supply an
argument to create a certain middleware, and that argument is
dependent on which Express app object is the owner of the Request. So
I need to access a property of the Request before it is passed to the
middleware that needs to be created using that property.
Your factory of dynamic middleware should return a new function
Functions that returns new functions, are also knows as curried functions, here is a practical example:
// based on 'someId' we will return a specific middleware
req.get('/user/:someId',
createDynamicMiddleware,
(req,res) => {
res.send('ok')
})
function createDynamicMiddleware(req,res,next){
if(req.params.someId === 'me') {
return function(req, res, next){
// do some stuff if someId is me
return next();
}
} else {
return function(req, res, next){
// do some other stuff otherwise
return next();
}
}
}
Q3: How are the Request/Response arguments passed to the next middleware when you call next() without any arguments?
Express handle this for you!
Also, if you add a property to req or res objects, it will be present in the next middlewares
For example:
router.get('/', (req,res,next) => {
req.customProperty = 'hello!';
return next();
}, (req, res) => {
console.log(req.myCustomProperty)
// hello!
})

Express: Render partially in each handler?

I have a modest little site that uses Express and Pug. Every page has a navbar that contains the user's name drawn from a user parameter and the main content of the page is below which uses a data parameter. The main content is rendered from a template that extends the root template. Both need their own parameter to render properly.
Is there a way to partially render a view in each handler?
var app = require('express')()
app.set('view engine', 'pug')
app.get('/', (req, res, next) => {
var userdata = '...'
req.render('/index', {user: userdata}
next() // the request is for /page
})
app.get('/page', (req, res, next) => {
var moredata = '...'
req.render('./page/index', {data: mroedata})
})
AFAIK that's not possible, but as an alternative, you can use res.locals to achieve something similar:
let userMiddleware = (req, res, next) => {
let userdata = '...';
res.locals.user = userdata;
next();
});
app.use(userMiddleware);
// This can stay the same:
app.get('/page', (req, res, next) => {
var moredata = '...'
req.render('./page/index', {data: mroedata})
})
This is a middleware that will make the user variable available to all of your templates, which seems to me is what you want.
Instead of applying it globally, you can also use it for specific routes:
app.get('/page', userMiddleware, (req, res) => ...);

Express all issue

I docs it said that this two way of defining two sequential midlewares are equivalent
http://expressjs.com/4x/api.html#app.all
app.all('*', requireAuthentication, loadUser);
Or the equivalent:
app.all('*', requireAuthentication)
app.all('*', loadUser);
But it if we have middlewares defined as:
var requireAuthentication = function(req, res, next){
req.params.someParam = 1
next()
}
var loadUser = function(req, res, next){
console.log('someParam', req.params.someParam)
...
}
In first case req.params.someParam inside loadUser will be 1, and in second case it will be undefined.
Is it a bug or wat?
In short: req.params doesn't persist across routes. Use res.locals instead.
Not a bug in Express, but not very intuitive. This is a good question!
req.params is for grabbing parameters out of a URL. For example, if you had a route defined like this...
app.get('/user/:id', function(req, res) {
res.send('user id == ' + req.params.id);
});
...and you visited /user/123, then req.params.id would be "123".
This params property is different for every route. Take a look at this example:
app.get('/user/:userid', function(req, res, next) {
// req.params.userid is defined in here
// req.params.id is NOT defined here
next();
});
app.get('/user/:id', function(req, res) {
// req.params.id is defined in here
// req.params.userid is NOT defined here
});
req.params isn't the same for every route, which explains what you're seeing.
Your first example has one route with two request handler functions, so they all have access to the same req.params object. Your second example has two routes with two request handler functions, so they're accessing different req.params objects.
If you're trying to persist data between different request handler functions across the same request, you should use res.locals. To rewrite your example:
var requireAuthentication = function(req, res, next){
res.locals.someParam = 1
next()
}
var loadUser = function(req, res, next){
console.log('someParam', res.locals.someParam)
...
}
Using res.locals will work whether you have one line or two.
Hope that helps!