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)
}
Related
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
});
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));
I am currently writing an express app and want to use some custom middleware that I have written however express keeps throwing an issue.
I have an es6 class that has a method that accepts the correct parameters like below:
foo(req, res, next){
console.log('here');
}
then in my app I am telling express to use it like so:
const module = require('moduleName');
...
app.use(module.foo);
but express keeps throwing this error:
app.use() requires middleware functions
any help would be greatly appreciated.
This error always occurs TypeError: app.use() requires middleware functions
Since you are not exporting that function that's why it's unreachable
try to export it like this from file
exports.foo=function(req, res, next){
console.log('here');
next();
}
You can also use module.exports
module.exports={
foo:function(req,res,next){
next();
}
}
The solution has two parts. First make the middleware function a static method of that class that you export from your module. This function needs to take an instance of your class and will invoke whatever methods you need to.
"use strict";
class Middle {
constructor(message) {
this._message = message;
}
static middleware(middle) {
return function middleHandler(req, res, next) {
// this code is invoked on every request to the app
// start request processing and perhaps stop now.
middle.onStart(req, res);
// let the next middleware process the request
next();
};
}
// instance methods
onStart(req, res) {
console.log("Middleware was given this data on construction ", this._message);
}
}
module.exports = Middle;
Then in your node JS / express app server, after requiring the module, create an instance of your class. Then pass this instance into the middleware function.
var Middle = require('./middle');
var middle = new Middle("Sample data for middle to use");
app.use(Middle.middleware(middle));
Now on every request your middle ware runs with access to the class data.
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.
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) {