Scope is working a bit strange inside an express route - node.js

I got the following issue: the options variable in the express router is not using the correct options variable. Any suggestions?
router.use('/test1', new Factory("test1") );
router.use('/test2', new Factory("test2") );
function Factory(options) {
router.use((req,res,next) => {
res.json(options)
})
return router
};
/*
Returns :
/test1 "test1"
/test2 "test1" ???
*/

function Factory(options) {
return (req,res,next) => {
res.json(options)
})
};
router.use('/test1', new Factory("test1") );
router.use('/test2', new Factory("test2") );

tl;dr;
Use this instead.
router.get("/:options", Factory);
function Factory(req, res, next){
const options = req.params.options;
res.json(options);
}
Why what you have does not work as expected
In your Factory you do this:
router.use((req, res, next) => {
res.json(options);
});
This statement tells express: hey whenever a request comes in then run this middleware:
(req, res, next) => {
res.json(options);
})
Note that you do not specify a route for this, it's just in the format app.use(handler), so it will run for every request.
above that you do this:
router.get("/test1", new Factory("test30"));
Now when express sees this it says, oh here's a handler for /test1, let me register that handler to that route. When it registers the handler it comes across this expression: new Factory("test1"). Note that this is an expression and will get executed when the route is registered and NOT when handling a request. It can essentially be rewritten like this: router.get("/test1", Factory("test30")), which results in:
router.get("/test1", router.use((req, res, next) => {
res.json("test1");
return router;
}));
This part:
router.get("/test1", )
depends on this part:
router.use((req, res, next) => {
res.json("test1");
return router;
})
Which means that the latter will be executed first. This means that at the end of it all your code actually looks something like this:
const resultOfThisLine =
router.use((req, res, next) => {
res.json("test1"); //You passed test1 as options to the factory
return router;
});
router.get("/test1", resultOfThisLine);
router.get("/test2", router.use((req, res, next) => {
res.json("test1"); //You passed test1 as options to he factory
return router;
}));
As you can see,
router.use((req, res, next) => {
res.json("test1"); //You passed test1 as options to the factory
return router;
});
is actually registered before everything else and since it returns a response, nothing else will ever get called. Moreover, this handler will respond to ANY request because there is no specific url attached to it.

Thanks for the quick reactions. It made me realize I need to create a new router object.
Like so:
const express = require('express')
const router = express.Router();
router.use('/test1', new Factory("test1"));
router.use('/test2', new Factory("test2"));
function Factory(options) {
const router2 = express.Router();
router2.get("", handleRoute.bind({ options: options }))
router2.post("", handleRoute.bind({ options: options }))
function handleRoute(req, res, next) {
res.json({ message: "hello", options: options })
}
return router2
};

Related

Express - using application-level middleware conditionally on route-level

I'm trying to understand how to use an application-level middleware (or at least usually used like this) like cookie-parser on route-level and conditionally.
I tried something like:
const myMiddleware = (req, res, next) => {
if (myCondition) {
return cookieParser();
} else {
next();
}
}
app.use('/admin', myMiddleware, (req, res) => {
res.sendStatus(401)
})
But it's not working, the request will be just stuck.
Is this possible?
Traditional cookie-parser implementation:
app.use(cookieParser())
cookieParser() returns a middleware function, i.e. a function that takes in req, res, next as arguments. You just have to pass it the arguments:
const cookieParserMiddleware = cookieParser();
const myMiddleware = (req, res, next) => {
if (myCondition) {
return cookieParserMiddleware(req, res, next);
}
next();
};
app.use("/admin", myMiddleware, (req, res) => {
res.sendStatus(401);
});
Notice that I'm creating the cookieParser middleware outside myMiddleware - technically we could also just do return cookieParser()(req, res, next) but recreating the same middleware again and again on every request would be wasteful.
I've also removed the else since the if block returns from the function (guard clause).

prevent express middleware from executing for same parent path

This is my code when.
I am hitting put API it is executing middleware 3 times but it should execute for put API only.
app.use('/api/user', MiddlewareFun);
app.get('/api/user', (req, res) => {
//do something
});
app.use('/api/user', MiddlewareFun);
app.post('/api/user', (req, res) => {
//do something
});
app.use('/api/user', MiddlewareFun);
app.put('/api/user', (req, res) => {
//do something
});
please don't say use like this.
app.put('/api/user', MiddlewareFun, (req, res) => {
//do something
});
Well, it's happening, because you've made it to. If you want the middleware, to be executed at only selected method, you have to specify it. For example:
Instead of doing:
app.use('/api/user', MiddlewareFun)
app.put('/api/user', (req, res) => {
//do something
})
replace use method with put. As you'd bind regular route:
app.put('/api/user', MiddlewareFun)
app.put('/api/user', (req, res) => {
//do something
})
Also, one more thing. You don't have to duplicate your middleware call before every route declaration. If you want to apply a middleware to your whole router, you can use .use() (as you did), or .all(); which will result in the same behavior.
The middlewares in Express are binded to app or to router.
The solution to yuur problem is to check the method of the request object at the middleware
let MiddlewareFun = function (req, res, next) {
if (req.method === 'PUT') {
// do something
}
next()
}
app.use('/api/user', MiddlewareFun);
The answer is, You need to write express middleware which is part of your app or router. You can write as many middlewares you want, but in your case you just need it only once and here is the implementation of that.
const MiddlewareFun = function(req, res, next) {
// req is object which consist of information about request made.
// From req object you can get method name which is called.
if(req.method.toLowerString() === 'put') {
// your logic goes here
}
next();
}
app.use('/api/user', MiddlewareFun);
app.get('/api/user', (req, res) => {
//do something
});
app.post('/api/user', (req, res) => {
//do something
});
app.put('/api/user', (req, res) => {
//do something
});

How can I define express middleware for all routes

I am trying to define one global middleware which will work for all routes of my app. I tried some ways but still got some issues.
var _gMDLW = function (req, res, next) {
if(req.route) console.log('Called route ', req.route.path);
next();
}
// Working fine and result on _gMDLW is /route1
app.get('/route1', _gMDLW, function (req, res, next) { return res.sendStatus(200); })
var globalRouter = new express.Router()
// Working fine and result on _gMDLW is /view
globalRouter.route('/view')
.get(_gMDLW, function (req, res, next) { return res.sendStatus(200);})
app.use(globalRouter);
But problem is here
// Error in _gMDLW and getting /list instead of /items/list
var itemRouter = new express.Router()
itemRouter.route('/list')
.get(_gMDLW, function (req, res, next) { return res.sendStatus(200);})
app.use('/items', itemRouter)
Second Question is is there any way to define/add _gMDLW inside app instead of adding in each route something like app.use(_gMDLW) ?
Thank you
You can use app.all() to resolve this issue
Example
app.all('*', _gMDLW);
function _gMDLW(req, res, next) {
if (req.path == '/') return next();// redirect to homepage for guest
next();//authenticated user
}
You can modify it as your requirement

Express middleware - router.use issue

I have this simple code:
var express = require('express');
var router = express.Router();
router.get('/', function (req, res, next) {
req.lectalApiData = {
Model: Email,
conditions: req.query
};
router.use(function(req,res,next){ //this is not executing
console.log('do that')
res.json({ok:'OK'});
});
});
I clearly am doing something wrong, but according to the docs, it says I can use this syntax:
http://expressjs.com/guide/routing.html
what am I doing wrong?
perhaps it's because the router.use is nested inside a router.get -
so my question then becomes - how do I create more middleware for that same route inside the router.get middleware?
Just keep adding functions to router.get('/',, they get executed in order. Don't forget to call next.
router.get('/', function (req, res, next) {
req.lectalApiData = {
Model: Email,
conditions: req.query
};
next(); // pass off to next middleware
}, function(req,res,next){
console.log('do that')
res.json({ok:'OK'});
});
or better:
function doThis(req, res, next) {
req.lectalApiData = {
Model: Email,
conditions: req.query
};
next(); // pass off to next middleware
}
function doThat(req, res) {
console.log('do that')
res.json({ok:'OK'});
}
router.get('/', doThis, doThat);

How can I trigger a route handler manually in express js?

suppose I have a simple express js application like the following:
var express = require('express');
var app = express();
app.get('/', function(req, res) {
return res.json({ hello: 'world' });
});
module.exports = app;
I want to be able to go to the command line, require the app, start the server and simulate a request. Something like this:
var app = require('./app');
app.listen(3000);
app.dispatch('/') // => {hello:"world"}
You can use run-middleware module exactly for that. This is working by creating new Request & Response objects, and call your app using those objects.
app.runMiddleware('/yourNewRoutePath',{query:{param1:'value'}},function(responseCode,body,headers){
// Your code here
})
More info:
Module page in Github & NPM;
Examples of use run-middleware module
Disclosure: I am the maintainer & first developer of this module.
These two options worked for me without any error:
option 1
app.post('/one/route', (req, res, next) => {
req.url = '/another/route'
req.method = 'GET'
next();
});
app.get('/another/route', (req, res) => {
console.log("Hi, I am another route");
});
option 2
app.post('/one/route', (req, res, next) => {
req.url = '/another/route'
req.method = 'GET'
app._router.handle(req, res, next);
});
app.get('/another/route', (req, res) => {
console.log("Hi, I am another route");
});
Express : 4.15.4
No extra library or npm module is required
this solution works perfectly by using express 4.16
(and optionally -express promise router , which is great wrapper for error handling)
its straightforward, without using router inside a router nor re-write the request, like in the other suggested answers
just change the URL in the request and return it to router handle function
const router = require('express-promise-router')();
router.post('/signin', function(req, res , next) {
if (req.body.Username === 'admin') {
req.url = '/admin/signin'
router.handle(req, res, next)
}
else { // default doctor
req.url = '/doctors/signin'
router.handle(req, res, next)
}
});
router.post('/doctors/signin',someCallback1)
router.post('/admin/signin',someCallback1)
As far as I am aware, there isn't a way to switch to a specific route internally, but there is a way to flag the request, then move on to the next route:
app.use((req, res, next) => {
if("nextRouteCondition"){
req.skip = true;
return next();
}
})
This may allow you to accomplish what you want to do.

Resources