Express middleware - router.use issue - node.js

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);

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).

Scope is working a bit strange inside an express route

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
};

How to pass multiple parameters from controller in node.js express

I am working with Passport, and I need to pass multiple parameters through to from my controller to my router. Basically it only passes the first one.
I want to get
app.get('/auth/steam', controllers.auth.authenticate);
to result in
app.get('/auth/steam', passport.authenticate('steam'), function(req, res) { res.render('index') };);
Right now it only loads the 1st parameter.
My controller looks like this
exports.authenticate =
passport.authenticate('steam'),
function(req, res) {
res.render('index');
};
How would I do this?
EDIT: I want to only be able to call it with controllers.auth.authenticate, not in an array like: controllers.auth.authenticate[0]!
Warning NOT tested.
You can wrap all inside function
exports.authenticate = function(req, res, next) {
passport.authenticate('steam', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/auth/steam'); }
res.render("index");
});
}
Or you can use router and protect ALL verbs (get, post, etc)
var express = require('express');
var router = express.Router();
router.use(function (req, res, next) {
passport.authenticate('steam');
});
router.get('/', function(req, res, next) {
res.render("index");
});
module.exports = router;
And use router on the app
var ctrl = require("yourModuleName");
app.use('/auth/steam', ctrl); // app.use NOT app.get
Other alternative is to protect only the get
var express = require('express');
var router = express.Router();
router.get('/', passport.authenticate('steam'), function(req, res, next) {
res.render("index");
});
module.exports = router;
var ctrl = require("yourModuleName");
app.use('/auth/steam', ctrl); // app.use NOT app.get
See Express routing page

Chaining multiple pieces of middleware for specific route in ExpressJS

I want to just verify something but have't been able to find anything in the Express docs or online regarding this (although I know it's a feature).
I could just test this out but I don't really have a nice template and would like to hear from the community.
If I define a route in express like such:
app.get('/', function (req, res) {
res.send('GET request to homepage');
});
I can also define a middleware and load it directly, such as
middleware = function(req, res){
res.send('GET request to homepage');
});
app.get('/', middleware)
However, I can also chain at least one of these routes to run extra middleware, such as authentication, as such:
app.get('/', middleware, function (req, res) {
res.send('GET request to homepage');
});
Are these infinitely chainable? Could I stick 10 middleware functions on a given route if I wanted to? I want to see the parameters that app.get can accept but like mentioned I can't find it in the docs.
Consider following example:
const middleware = {
requireAuthentication: function(req, res, next) {
console.log('private route list!');
next();
},
logger: function(req, res, next) {
console.log('Original request hit : '+req.originalUrl);
next();
}
}
Now you can add multiple middleware using the following code:
app.get('/', [middleware.requireAuthentication, middleware.logger], function(req, res) {
res.send('Hello!');
});
So, from the above piece of code, you can see that requireAuthentication and logger are two different middlewares added.
It's not saying "infinitely", but it does say that you can add multiple middleware functions (called "callbacks" in the documentation) here:
router.METHOD(path, [callback, ...] callback)
...
You can provide multiple callbacks, and all are treated equally, and behave just like middleware, except that these callbacks may invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to perform pre-conditions on a route then pass control to subsequent routes when there is no reason to proceed with the route matched.
As you can see, there's not distinction between a middleware function and the function that commonly handles the request (the one which is usually the last function added to the list).
Having 10 shouldn't be a problem (if you really need to).
Express version "express": "^4.17.1" or above
From the document: Series of Middleware
var r1 = express.Router();
r1.get('/', function (req, res, next) {
next();
});
var r2 = express.Router();
r2.get('/', function (req, res, next) {
next();
});
app.use(r1, r2);
Let's try a real life example:
tourController.js
exports.checkBody = (req, res, next)=>{ // middleware 1
if (!req.body.price){
return res.status(400).json({
status:'fail',
message:'Missing price!!!'
})
}
next();
}
exports.createTour = (req, res) => { // middleware 2
tours.push(req.body);
fs.writeFile(
`${__dirname}/dev-data/data/tours-simple.json`,
JSON.stringify(tours),
(err) => {
res.status(201).json({
status: 'success',
data: {
tour: newTour,
},
});
}
);
};
tourRouter.js
const express = require('express');
const tourController = require('./../controller/tourController')
const router = express.Router();
router.route('/')
.get(tourController.getAllTours)
.post(tourController.checkBody, tourController.createTour);
//muliple Middleware in post route
module.exports = router //need this or the following step will break
app.js
const express = require('express');
const tourRouter = require('./route/tourRouter');
const app = express();
app.use(express.json());
app.use('/api/v1/tours', tourRouter);
module.exports = app;

next() not working in Express 4

this is my app.js
function requireLogin(req, res, next) {
if (req.isAuthenticated()) {
next();
} else {
res.redirect("/");
}
}
/**
* Routes
*/
var index = require('./routes/index');
var dashboard = require('./routes/dashboard');
app.use('/', index);
app.use('/dashboard', requireLogin, dashboard);
routes/dashboard.js
var express = require('express');
var router = express.Router();
router.route('/')
.get(function (req, res, next) {
res.render('dashboard/index', {});
});
module.exports = router;
After doing the login I am directed to the route /dashboard, but I get a status 404.
If I try to remove the function requireLogin, the route /dashboard is visible and working.
Why?
I did some tests, and I saw that the problem is next().
For the login I used passport is working well.
If you still haven't figured out you can put return next() instead of next().
So this:
function requireLogin(req, res, next) {
if (req.isAuthenticated()) {
next();
} else {
res.redirect("/");
}
}
Should become this:
function requireLogin(req, res, next) {
if (req.isAuthenticated()) {
return next();
} else {
res.redirect("/");
}
}
Hope I helped!
Actually I had a very similar issue, for me it wasn't anything to do with the middleware that was handling authentication, it was the way my routes were set up.
I think you are trying to receive GET requests on a route you want to POST credentials to in dashboard.js:
router.route('/')
.get(function (req, res, next) {
res.render('dashboard/index', {});
});
should be:
router.route('/')
.post(function (req, res, next) {
res.render('dashboard/index', {});
});
since you are posting credentials to that route.

Resources