How to setup multi-choice route in express app? - node.js

I have defined a route in my express app as such :
app.get('/ts/:space/:mode/:param1/:param2/:fromdate/:todate',(req,res,next) =>{...})
But now I would want to add extra parameters if the :space parameter is equal to a specific value. For example, if :space is blah then the route should transform into
app.get('/ts/:space/:mode/:param1/:param2/:param3/:fromdate/:todate',(req,res,next) =>{...})
Is that possible without hard-coding the blah keyword and putting the hard-coded path before the generic one ?

The addition of extra parameter renders it as a new distinct route, so I think performing a redirect to that new route whenever you encounter :space = blah should satisfy your requirement.
res.redirect()
Added code as per OP request
app.get('/ts/:space/:mode/:param1/:param2/:fromdate/:todate', (req, res, next) => {
let param3 = 'sample';
if (req.params.space === 'blaah') {
res.redirect(`/ts/${req.params.space}/${req.params.mode}/${req.params.param1}/${req.params.param2}/${param3}/${req.params.fromdate}/${req.params.todate}`);
}
});

Split your handler functions to isolate functions.
app.get('/ts/:space/:mode/:param1/:param2/:fromdate/:todate', handler1)
app.get('/ts/:space/:mode/:param1/:param2/:param3/:fromdate/:todate', handler2)
and trick to handler1 special case:
const handler1 = (req,res,next) => {
if (req.params.space === "blah") {
// use hanler for `/ts/:space/:mode/:param1/:param2/:param3/:fromdate/:todate` route
// force set param3 value,
req.params['param3'] = "maybe_a_default_value";
return handler2(req,res,next);
}
// handler for `/ts/:space/:mode/:param1/:param2/:fromdate/:todate` route
}
const handler2 = (req,res,next) => {
// handler for `/ts/:space/:mode/:param1/:param2/:param3/:fromdate/:todate`
// now, req.params.space === "blah" and req.params.param3 === "maybe_a_default_value"
}

Related

nodejs condense a validation of json body request against regex

Is there a condensed way to do this validation on nodejs
//inbound express request
{ "myVal":"abcdefghij" }
/* first test if value exists,
then if it fails a regex return an error & exit function
if pass then continue
*/
// ... logic to get express.post(...=> {
if (( req.body.myVal == null ){
// no value, send error response and exit function
}else{
const inVar = req.body.myVal;
if ( inVar ){
const regex = /^([a-j0-9]){1,11}$/;
if(regex.test(inVar ) == false){
res.send({ error: "failed regex" });
res.end();
return;
}
... else continue
Here are a few options:
Use express-validator or a similar package to validate your endpoint params and/or body in the endpoint configuration. This nicely separates validation from the logic of the endpoint and is extensible to your other endpoints without having to maintain code in each one.
Check out the package here:
Express-validator
Specifically, what you might want is their custom validator:
Express-validator's custom validation docs
Example usage:
const { body } = require('express-validator');
const MY_REGEX = /^([a-j0-9]){1,11}$/;
app.post('/my-endpoint',
body('myVar').custom(value => {
if(!MY_REGEX.test(value)) Promise.reject("Error: invalid value for 'myVar.'")
}),
(req, res) => {
// Business logic only, no validation
},
);
You could even take the function that's inside .custom(), and put it in another file, import it here, and reuse it elsewhere.
If you want to do the validation in the handler, there are a few ways to make your code more brief/safe/nice.
Set your regexp variable in a different file or as a constant at the top of the file (descriptively) or in a different constants/utils file.
"Error gate" by returning after you find an error. Avoid nested if/else.
Is the error the same for missing value and incorrect value? If so, your regexp will error appropriately for undefined, empty string, and null anyway. consider combining them in the same if statement.
Example:
// imports
// ...
const MY_DESCRIPTIVE_REGEX_NAME = /^([a-j0-9]){1,11}$/;
//...
(req, res) => {
if(!MY_DESCRIPTIVE_REGEX_NAME.test(req.body.myVal) {
res.status(400);
return res.send({ error: "MyVal must be between 1 and 11 characters and contain only numbers and letters between 'a' and 'j'."});
}
// rest of logic, not in an if statement
}

Is it possible to delete anchor tags from a CRUD created with Express?

I am creating a simple CRUD Board through Express.
I implemented the CRU, but the delete function failed. I used Rails method='delete' as a common anchor tag, but Express does not seem to support it.
How can I activate the delete link?
app.js
...
const board = require("./routes/board");
app.use("/board", board);
...
views
a(href=`/board/${board._id} method="delete"`) 삭제
routes
...
const board = require("../logic/board");
router.delete("/:id", board.delete);
...
logic
...
const Board = require("../db/board");
exports.delete = (req, res) =>{
Board.findByIdAndRemove(req.params.id, err => {
if (err) {
return next(err);
}
res.redirect("/board/index");
});
}
...
And I want to ask. What is the difference in behavior between doing something like exports.delete = () => {} and doing something like module.exports = logic <delete, create etc...>?
HTML <a>nchor tags don't have a method attribute -- perhaps you're thinking of <form> tags?
What is the difference in behavior between doing something like exports.delete = () => {} and doing something like module.exports = logic
The difference is when your logic gets executed. The exports.delete = () => {} form will export a function that you can execute at some point in the future, whereas the module.exports = Board.findByIdAndRemove() will execute the database query immediately when the file is parsed (which you probably don't want).

ExpressJS .param('term', ...) triggers when param not in route URL

I have two routes in an application:
app.post('/thing/add', thing.addHandler);
app.post('/thing/:thingId/edit', thing.editHandler);
(Order as listed)
The edit handler has a :thingId parameter that I find and populate using the following (simplified for example):
app.param('thingId', function ( req, res, next, id ) {
Thing.findById(id, function ( err, foundThing ) {
...
req.thing = foundThing;
next();
});
});
The .param('thingId') fires when I post to either route.
Any idea why? I thought perhaps because the word thing is a substring of thingId and might be getting matched.
If I use regex for the term:
app.param(/^thingId$/, function ( req, res, next, id ) {..}
it works as I had intended it to.
Express sees your post to /thing/add and thinks that thingId now has the value 'add'. It doesn't know that thingId is supposed to be an integer value.
To fix this, figure out how to make the routes unambiguously different.
Recomend use PUT method for update or modify documents.
I think you need some like this, bro:
app.post('/thing', function(req,res,next){
new Thing();
return next();
});
app.put('/thing/:id', function(req,res,next){
var thingId = req.params.id
Thing.findById(id, function ( err, foundThing ) {
req.thing = foundThing;
next();
});
});

Which is the right way to extend ExpressJS/Response object with custom methods?

At now I do extending of request object via middleware:
function(req, res, next) {
res.customMethod = function() {
}
next();
}
But I think it's not correct due polluting of res.prototype namespace. Does anybody know better approach or maybe expressjs4 already have any approach for this?
You are almost perfect in your approach but as you said is better to avoid polluting the res namespace: to reach your goal you can use the res.locals property which is designed for this reason. What follows is a snippet where I attach to the response object the translator of my application.
app.use(function(req, res, next){
function Translator (lang) {
this.lang = lang ? lang : 'it';
this.translate = function (sentence) {
var d = dictionaries.get;
var translation = d[this.lang] ? d[this.lang][sentence] : d['it'][sentence];
return translation ? translation : sentence;
};
this.setLang = function (l) {
this.lang = l;
};
};
res.locals.translator = new Translator();
next();
});

Remove route mappings in NodeJS Express

I have a route mapped as:
app.get('/health/*', function(req, res){
res.send('1');
});
How can I remove / remap this route to an empty handler at runtime?
This removes app.use middlewares and/or app.VERB (get/post) routes. Tested on express#4.9.5
var routes = app._router.stack;
routes.forEach(removeMiddlewares);
function removeMiddlewares(route, i, routes) {
switch (route.handle.name) {
case 'yourMiddlewareFunctionName':
case 'yourRouteFunctionName':
routes.splice(i, 1);
}
if (route.route)
route.route.stack.forEach(removeMiddlewares);
}
Note that it requires that the middleware/route functions have names:
app.use(function yourMiddlewareFunctionName(req, res, next) {
... ^ named function
});
It won't work if the function is anonymous:
app.get('/path', function(req, res, next) {
... ^ anonymous function, won't work
});
Express (at least as of 3.0.5) keeps all of its routes in app.routes. From the documentation:
The app.routes object houses all of the routes defined mapped by the associated HTTP verb. This object may be used for introspection capabilities, for example Express uses this internally not only for routing but to provide default OPTIONS behaviour unless app.options() is used. Your application or framework may also remove routes by simply by removing them from this object.
Your app.routes should look similar to this:
{ get:
[ { path: '/health/*',
method: 'get',
callbacks: [Object],
keys: []}]
}
So, you should be able to loop through app.routes.get until you find what you are looking for, and then delete it.
The above approach requires you have a named function for the route. I wanted to do this as well but didn't have named functions for routes so I wrote an npm module that can remove routes by specifying the routing path.
Here you go:
https://www.npmjs.com/package/express-remove-route
It is possible to remove mounted handlers (added with app.use) while the server is running, although there is no API to do this, so it isn't recommended.
/* Monkey patch express to support removal of routes */
require('express').HTTPServer.prototype.unmount = function (route) {
for (var i = 0, len = this.stack.length; i < len; ++i) {
if (this.stack[i].route == route) {
this.stack.splice(i, 1);
return true;
};
}
return false;
}
This is something I need, so it's a shame there isn't a proper api, but express is just mimicing what connect does here.
app.get$ = function(route, callback){
var k, new_map;
// delete unwanted routes
for (k in app._router.map.get) {
if (app._router.map.get[k].path + "" === route + "") {
delete app._router.map.get[k];
}
}
// remove undefined elements
new_map = [];
for (k in app._router.map.get) {
if (typeof app._router.map.get[k] !== 'undefined') {
new_map.push(app._router.map.get[k]);
}
}
app._router.map.get = new_map;
// register route
app.get(route, callback);
};
app.get$(/awesome/, fn1);
app.get$(/awesome/, fn2);
And then when you go to http://...awesome fn2 will be called :)
Edit: fixed the code
Edit2: fixed again...
Edit3: Maybe simpler solution is to purge routes at some point and repopulate them:
// remove routes
delete app._router.map.get;
app._router.map.get = [];
// repopulate
app.get(/path/, function(req,res)
{
...
});
You can look into Express route middleware and possibly do a redirect.
As already mentioned above, the new Express API doesn't seem to support this.
Is it really necessary to completely remove the mapping? If all you need is to stop serving a route, you can easily just start returning some error from the handler.
The only (very odd) case where this wouldn't be good enough is if dynamic routes were added all the time, and you wanted to completely get rid of old ones to avoid accumulating too many...
If you want to remap it (either to do something else, or to map it to something that always returns an error), you can always add another level of indirection:
var healthHandler = function(req, res, next) {
// do something
};
app.get('/health/*', function(req, res, next) {
healthHandler(req, res, next);
});
// later somewhere:
healthHandler = function(req, res, next) {
// do something else
};
In my opinion this is nicer/safer than manipulating some undocumented internals in Express.
There is no official method but you can do this with stack.
function DeleteUserRouter(appName){
router.stack = router.stack.filter((route)=>{
if(route.route.path == `/${appName}`){
return false;
}
return true;
});
}
appName is path name .
Filter the methods in the router.route.stack where the router is express.Router
or you can do the same for the app but with app._router.stack.
Note: For below 4.0 use - app.router.stack.

Resources