Optional GET parameter in Express route - javascript

The following:
app.get('/foo/start/:start/end/:end', blah.someFunc);
matches
/foo/start/1/end/4
but I want it to also match an optional parameter
/foo/start/1/end/4/optional/7
I've tried this:
app.get('/foo/start/:start/end/:end(/optional/:value)?', blah.someFunc);
but it doesn't match either of the above two examples. I think it's because I'm trying to give it a RegExp when it's expecting something else?
Thanks.

Why don't you add another rule before the one you have, like this
app.get('/foo/start/:start/end/:end/optional/:value', blah.someFunc);
app.get('/foo/start/:start/end/:end', blah.someFunc);
It will be used before the one without the optional value.
If you want to use just one line try this:
app.get('/foo/start/:start/end/:end/optional?', blah.someFunc)
see the docs for an example.

If you are using Express 4.x Then I think its better to use array format for route.
For example I have route /service which gives all service list and same route when used with id /service/id/:id gives single service with id in the param.
app.get(['/service', '/service/id/:id'], function(req, res) {});

In this example, if the url is /hello or /hello/world it works. The ? makes the parameter become optional (express 4).
// app.js
var index = require('/routes/index');
app.use('/hello', index);
// routes/index.js
router.get('hello/:name?', function(req, res){
var name = req.params.name;
var data = {
name: name
};
res.json(data);
});

You can also use regular expressions in routes. Perhaps something like:
app.get(/^\/foo\/start\/:start\/end\/:end(\/optional\/:value)?/, function (req, res, next) {

Related

Is it possible to use the capture groups from the regex of an express route?

I'm working on a fairly large node/express app using the GOV.UK Prototyping Kit. I have separated multiple instances of the prototype into standalone versions for archival purposes e.g. this isn't a build it once 'DRY' approach but rather there's quite a bit of duplication as the prototypes evolve (sometimes significantly). The crucial thing is that each version e.g. /phase/release/ is suitably separated.
I've separated the routes quite nicely but the final thing that would help would be to have a dynamic route.use -> require so that I don't have to manually update the top-level routes.js file for each version release.
I use regex matching in my routes quite a lot so I'm fairly familiar with that, but what I'm wondering is how I can use the details of the match to dynamically build the require. Or perhaps use something in req.params - but I don't think that's available outside of a route.
Here's an outline of my code. The static route (R1) works fine but neither of the dynamic routes (R2/R3) work (when uncommented):
const express = require('express')
const router = express.Router()
router.use('/:phase/:release', function (req, res, next) {
// Make phase and release available in subsequent routes
let prototype = {}
prototype.phase = req.params.phase
prototype.release = req.params.release
req.session.data.prototype = prototype
next()
})
// Phase/release specific routes
// -----------------------------
// R1. This works
router.use('/test/a/', require('./views/test/a/_routes.js'));
// R2. But a dynamic version world be better?...
// router.use(/(alpha|beta|test)\/([abc])\//, require('./views/' + \1 + '/' + \2 '/_routes.js'));
// R3. ...or...
// router.use('/:phase/:release/', require('./views/' + req.params.phase + '/' + req.params.release '/_routes.js'));
Ultimately I think my approach is wrong and that it's not possible to reference the regex capture groups this way, but equally I don't think I can access the req object (and therefore req.params) as part of the require path.
I would expect there to be a way to use information known prior to making the require within the constructed require path but I just can't work out how.
Any input/help much appreciated.
Hope i understand your question correctly.
If the _routes.js exports a router which is essentialy a function which accepts req, res, next params then you can do this:
router.use('/:phase/:release/', function(req, res, next){
var anotherRouter = require('./views/' + req.params.phase + '/' + req.params.release '/_routes.js');
anotherRouter(req, res, next);
});
Express documentation for router.get documents in the last exemple that when you use a regex for the route parameter, the captured groups are available in req.params by position (tested on Express 4.17). This also works with a required router handler instead of a function (as you do), of course.
router.use(/(alpha|beta|test)\/([abc])\//, (req, res) => {
console(req.params[0]); // will show the first captured group: alpha, beta or test
console(req.params[1]); // will show the 2nd captured group
});

Expressjs middleware keeps variable changed

I am trying to do a simple thing which is obvious I believe in the code below:
module.exports = function(req, res, next) {
var dop = require('../../config/config').DefaultOptions;
console.log(require('../../config/config').DefaultOptions);
console.log(dop);
dop.firstPage = 'test.sjs';
next();
};
This is an Expressjs middle ware which is very simple but the interesting point is that next time I load a page both of the console.log results has been changed to 'firstPage: test.sjs'. It shouldn't act like this and it should only change the dop variable.
Anyone with the knowledge why this creepy thing is happening?
Thank you
The main issue is require() is cached, so require('../../config/config') returns reference to the same instance, and as a result changing in one place causes all other references and subsequent requires to get that modified instance.
The simplest solution would be have a function in config to return a config object, that way every time invoking the get config function you will get a new instance with essentially the same content. I.e.:
config.js:
module.exports = {
getDefaultOptions: function(){
return {foo: 'bar', ip: '1.1.1.1'}
}
};

Why is this Express middleware not being called with arguments?

I am working on a middleware that needs bodyParser to run, but I don't want to make the app bring that in as a dependency. Instead, I want to make a package that requires that and exports a middleware like this:
//routes.js
app.use('/', middlewareWrapper(thing));
//middlware.js
export function middlewareWrapper(thing) {
return function addBody(req, res, next) {
function othermiddleware(_req, _res) {
// do something with thing and _req
return next();
}
return bodyParser.json()(req, res, othermiddleware);
};
}
This looks like it would work, and the othermiddleware is called, but with no arguments.
I found another answer that addresses this in basically the same way (it's old, but JS still works the same way): https://stackoverflow.com/a/17997640/444871
Why is the othermiddleware being called with no args?
The problem is that the middleware returned by bodyParser.json() simply call next() like this (i.e. with no argument). Here you are passing othermiddleware as next to the middleware returned by bodyParser.json(). Therefore it does not contain any argument.
Also the bodyParser does not change the original reference of req/res object. So the main req/res object still refer to the same object. So you dont need arguments to be passed. You can simply use the same req/res object in your othermiddleware function too.
return function addBody(req, res, next) {
function othermiddleware() {
// You should be able to use req and res modified by bodyParser.
// You dont need arguments to be passed.
return next();
}
return bodyParser.json()(req, res, othermiddleware);
};
Cause you do
next();
without passing parameters. Usually express does sth like that:
bodyParser.json()(
req,
res,
() => {
othermiddleware(req,res,next);
}
);
Or you use some bind magic:
bodyParser.json()(req, res, othermiddleware.bind(this,req,res,next));

ExpressJS - Optional part of the URL which is not a param?

I have a route like this: /products/123/versions/456.
I want the sub resource to be optional, so I want one route path to handle both /products/123 and /products/123/versions/456.
This doesn't work: /products/:pid/versions?/:vid?.
How do I make the /versions/ part optional without making it a param with :..?
You can make a named handler and use it for two routes:
function yourhandler(req, res) {
// ...
}
app.get('/products/:pid', yourHandler);
app.get('/products/:pid/versions/:vid', yourHandler);
Or you can fight with regular expressions and optional parts. But those are really two different routes, not one, and it just happens that you want to use the same handler for both of them so using two app.get() (or whatever HTTP method it is) route definitions is the clearest way to solve it.
You can also add this if you need it as well:
app.get('/products/:pid/versions', yourHandler);
app.get('/products/*', (req, res, next){
const path = req.path;
//now use any logic on path as string
if(path.includes('versions')){
console.log('run version handler');
}else{
console.log('run else handler')}
}
//or const [product, version] = /\/products\/(.*)\/versions\/(.*)/.exec(path)
}

nodejs connect middleware function override

does the connect middleware prevent a middleware component from accidentally override a function that was a method of res? OR you just have to make sure you name it differently?
So in this example you basically just messed up the body data, whats the best way to prevent this?
.use(connect.bodyParser())
.use(function(req,res,next){
req.body=null;
})
.use(function(req,res){
res.end(req.body);
});
You can put everything in your own namespace if you're very worried about it:
app.use(function (req, res, next) {
req.myappname.foo = null
req.myappname.bar = null
});
Or just make absolutely sure it doesn't exist before you overwrite it. You can always do a manual check in a test script.

Categories

Resources