Selectively apply middleware in express - node.js

I have a route /users as a parent suffix in my router, all subsequent routes will append the the parent eg. /users/details
In my app.js
app.use('/api/v1/users', userRoutes);
In my userRoutes
import express from 'express';
import users from '../controllers/user_controller';
import { authenticateRoute, authenticateSignedRoute, aclAuthenticator } from './../middlewares/AuthenticationMiddleware';
const router = express.Router();
//user routes
router.get('/details', authenticateRoute, aclAuthenticator, users.getDetails);
router.get('/posts', authenticateRoute, aclAuthenticator, users.getPosts);
module.exports = router;
WHAT I WOULD LIKE TO DO
Is there a way for me to add the authenticateRoute and the aclAuthenticator middleware to the parent prefixed route, and then for one particular route have an exception where only a third middleware is applied and not the first two.
For eg
app.use('/api/v1/users', authenticateRoute, aclAuthenticator, userRoutes);
My new router file
router.get('/details', applyOnlyThisMiddleWare, users.getDetails);
router.get('/posts', No MiddleWareAtAll, users.getPosts);
I'm basically trying to overide the initial middleware, is this possible?

This is how i explicitly disable middleware for specific routes
'use strict';
const ExpressMiddleware = ( req, res, next ) => {
// dont run the middleware if the url is present in this array
const ignored_routes = [
'/posts',
'/random-url',
];
// here i am checking for request method as well, you can choose to remove this
// if( ! ignored_routes.includes(req.path) ) {
if( req.method === 'GET' && ! ignored_routes.includes(req.path) ) {
// do what you gotta do.
// next();
}
else {
next();
}
}
export default ExpressMiddleware;
And in your server/routes file
app.use( ExpressMiddleware );
Ofcourse you might have to change the code, if you're using dynamic routes.. but that shouldn't be difficult.

The only way I know of to do that is to apply the first two middlewares directly to your router with no path prefix:
router.use(middleware1, middleware2);
Then, in each of those middlewares, check the path of the URL and if it is the special path that you want to skip those middlewares for, then just call next().
if (req.path.indexOf("/somepath") === 0) { return next() };
Then, you can register your third middleware only for the path you are interested in:
router.use("/somepath", middleware3);
The first two middewares will skip the ones you want them to skip and the third one will only get called for your specific route.

Related

Express subroute router, without having to use full path in each route

I'm splitting out part of the overall API as a separate subroute, i.e. (code simplified for this question)
const mainRouter = express.Router();
const bananaRouter = express.Router();
const appleRouter = express.Router();
bananaRouter.get('/banana/hello', (req, res) => { /* ... */ });
mainRouter.all('/banana/*', bananaRouter);
mainRouter.all('/apple/*', appleRouter);
This works. But I don't want to specify the full '/banana/hello' in every single route, and would much prefer '/hello'.
How can I do that?
Use .use() with a path prefix (no wildcard) instead of .all(). .use() will automatically match a path prefix without a wildcard whereas .get() and .all() will not and this will then set up your router better. So, do this to register the router:
// send all /banana prefixed urls to the bananaRouter
mainRouter.use('/banana', bananaRouter);
Then, inside of that router, you don't use the /banana part of the path:
// will match /banana/hello
bananaRouter.get('/hello', (req, res) => { /* ... */ });
In general, you will use .use() for routers and other middleware, not .all().

Express.js middleware executing for a route defined above it

From what I have read here and here, the order in which you place your middleware function matters, as you can have certain routes not go through the middleware function if it is placed before the route, and the routes which are placed after will go through this middleware function.
I am seeing mixed results as my dev environment is not respecting this and my prod environment is. The code is exactly the same.
What I am trying to do is have my login route not be protected by a token checker middleware function and have the rest of my routes protected by a token.
Here is my code:
routes.get('/login', function(req, res) {
// login user, get token
});
routes.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.headers['access-token'];
// decode token
if (token) {
// validate token
}
else if (req.method === 'OPTIONS') {
next();
}
else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
routes.get('/query/:keywords', function(req, res) {
console.log(req.params.keywords);
// execute query
});
app.use('/', routes);
the /query route is the only one that should have to go through the token middleware function correct? Right now I am getting the /login route also going through the token middleware function, which doesn't make sense as I shouldn't need to have a token to login.
Better yet, if there is a way to target which routes I want protected and which routes I do not want protected, this seems better than having to rely on an "order" of where the middleware function is placed.
First, follow along this usage in ExpressJS:
More than one callback function can handle a route (make sure you specify the next object). For example:
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the next function ...')
next()
}, function (req, res) {
res.send('Hello from B!')
})
You'll notice it's definition is close to what you're declaring on routes.use(yourFunction(...)). However, there's no real reason to do it this way other than following examples you've seen in documentation, which is a good way to start nevertheless.
However, it's a flimsy implementation, express will allow hierarchies within it's .get() .post() methods, that's correct, but this is a use case specific and not what you're looking for.
What you need is to implement your custom auth process using the double callback configuration. do this:
// You can save this function in a separate file and import it with require() if you want
const tokenCheck = function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.headers['access-token'];
// decode token
if (token) {
// validate token
}
else if (req.method === 'OPTIONS') {
next();
}
else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
routes.get('/login', function(req, res) {
// login user, get token [Unprotected]
});
routes.get('/query/:keywords', tokenCheck, function(req, res) {
console.log(req.params.keywords);
// execute query [Protected with tokenCheck]
});
app.use('/', routes);
You might need to play around with the code above, but it'll guide you on the right direction, this way, you can specify particular routes to execute the tokenCheck(req, res, next) function as you want.
The easiest way to do this is to use Router Middleware to scope Routes that require Authentication and the routes that don't. Since all Routers are Middleware, we can implement them just like any other middleware. Ensuring that we place the Routers and Routes in the order that we would like our Routes to be evaluated.
In the below example, the Express server has 2 routers, a LoginRouter and an ApiRouter.
LoginRouter - Generates a Token when receiving a request to POST /login and returns that to the requester for subsequent use in the /api routes.
ApiRouter - Wraps all other routers, centralizes middleware that needs to be globally applied to all routes under /api. Is only accessible to Authenticated Requests.
The API Router is only accessible if there is a token included in the Header and that token is obtained from the LoginRouter. LoginRouter has no authentication required.
With this setup, you'll keep adding routers after the Authorization Middleware to the API Router via .use() on the ApiRouter.
The below pattern of composing Routers from other Routers is very powerful, scalable and easy to maintain.
server.js
const express = require('express')
const bodyParser = require('bodyParser')
const ApiRouter = require('./routes/api')
const LoginRouter = require('./routes/login')
const port = process.env.PORT || 1337
const server = express()
server.use(bodyParser.json())
server.use('/login', LoginRouter)
server.use('/api', ApiRouter)
server.listen(port, () => console.log(`Listening on ${port}`))
LoginRouter - /routes/login.js
const router = require('express').Router()
router.post('/', (req, res) => {
// Validate Credentials
// some validation code...
// Then create the token for use later in our API
let token = '...'
// Response 200 OK with the token in the message body
return res.status(200).send({token})
})
module.exports = router
ApiRouter - /routes/api/index.js
const router = require('express').Router()
const UsersRouter = require('./routes/api/users')
router.use((req, res, next) => {
let authorizationHeader = req.headers['authorization'] || req.headers['Authorization'] // handle lowercase
let [, token] = authorizationHeader.split(' ')
if (!token) {
return res.sendStatus(403) // Forbidden, you're not logged in
} else {
// validate the token
if (!tokenIsValid) {
return res.sendStatus(403) // Forbidden, invalid token
}
// Everything is good, continue to the next middleware
return next()
}
})
router.use('/users', UsersRouter)
module.exports = router
UsersRouter - /routes/api/users
const router = require('express').Router()
router.get('/', (req, res) => {
// We only get here if the user is logged in
return res.status(200).json({users: []})
})
module.exports = router
The application of the token middleware should not happen to the login route due to route order and the fact the login route never calls the next object. Without more information we really can't trouble shoot what is happening beyond that however you could try inspecting it in your dev environment with a debugger break and looking at the req that hits that middleware.
We can however give you some information on how to try and isolate your .use middleware and how application of middleware order applies so that you can try and separate it from the login route entirely like in the bottom of your question.
When applying middleware to only specific routes you should keep note that order and .use are for middleware that should answer the request before telling express to continue looking for other middleware that come after them in the router that will also handle the request. If you only want it on a few routes, you can add it to only a few routes by being explicit like so:
router.get('/route', [ middleware1, middleware2, ..., middlewareX])
or
router.get('/route', middleware1, middleware2, ..., middlewareX)
both patterns will work. I however find the array pattern a little more palatable since I can define a lot of middle wares I want to apply and then concatenate new middleware for specific logic, and I only need modify where I declare that concatenation to add more functionality. It'd however rare to need that many middleware and you should be able to use either.
You could also section that middleware off to a subset of routes by using a router and applying it as the first middleware to the route chain before the router.
app.use('/user', authentication, userRouter)
or you can put it inside the router as the first middleware with a .use so that it handles all requests.
So remember the general tips about middleware usage:
order matters for middleware application
optional middleware that should be applied on route basis should be applied with the other middleware in order for only that route
error handling middleware must always come last, and have four arguments (err, req, res, next)
use routers to section .use middleware to specific routes and sets of routes
You can find more information about it in the expressjs documentation for middleware

Communication between middleware and route in keystonejs

I am using keystone and I have productDetail route in which I can add variables in res.locals to be used in templates. Is there a way I can use res.locals (of route file) in middleware.js file? As right now middleware is executing before route, I want route file to be executed first.
This is where middleware is executing in index.js file
keystone.pre('routes', middleware.initLocals);
And after that we have
exports = module.exports = function(app) {
// Views
app.get('/', routes.views.index);
app.get('/product-detail/:product', routes.views.productDetails);
}
I'm not sure if I got your question but this might help. You can run as many custom middleware you want after the middleware.initLocals (which apparently runs first). In your routes/middleware.js file, you can have, for example, two middleware:
exports.middleware0 = function (req, res, next) {
// Do some stuff
next();
};
exports.middleware1 = function (req, res, next) {
// Do some other stuff
next();
};
Then, inside your routes/index.js you can chain middleware together:
//...
var middleware = require('./middleware');
//...
exports = module.exports = function (app) {
// Use the middleware0 and middleware1:
app.get('/product-detail/:product', [middleware.middleware0, middleware.middleware1], routes.views.productDetails);
};

Proper way to remove middleware from the Express stack?

Is there a canonical way to remove middleware added with app.use from the stack? It seems that it should be possible to just modify the app.stack array directly, but I am wondering if there is a documented method I should be considering first.
use actually comes from Connect (not Express), and all it really does is push the middleware function onto the app's stack.
So you should be just fine splicing the function out of the array.
However, keep in mind there is no documentation around app.stack nor is there a function to remove middleware. You run the risk of a future version of Connect making changes incompatible with your code.
This is a useful functionality if you are inheriting some unwanted middleware from a framework built on express.
Building on some of the answers that came before me: In express 4.x the middleware can be found in app._router.stack. Note that the middleware are invoked in order.
// app is your express service
console.log(app._router.stack)
// [Layer, Layer, Layer, ...]
Tip: You can search the individual layers for the one you want to remove/move
const middlewareIndex = app._router.stack.findIndex(layer => {
// logic to id the specific middleware
});
Then you can just move/remove them with standard array methods like splice/unshift/etc
// Remove the matched middleware
app._router.stack.splice(middlewareIndex, 1);
There seems to be no built in way to do that, but you can manage to get the same result with a small trick. Create your own array of middleware (let's call it dynamicMiddleware) but don't push that into express, instead push just 1 middleware that will execute all the handlers in dynamicMiddleware asynchronously and in order.
const async = require('async')
// Middleware
const m1 = (req, res, next) => {
// do something here
next();
}
const m2 = (req, res, next) => {
// do something here
next();
}
const m3 = (req, res, next) => {
// do something here
next();
}
let dynamicMiddleware = [m1, m2, m3]
app.use((req, res, next) => {
// execute async handlers one by one
async.eachSeries(
// array to iterate over
dynamicMiddleware,
// iteration function
(handler, callback) => {
// call handler with req, res, and callback as next
handler(req, res, callback)
},
// final callback
(err) => {
if( err ) {
// handle error as needed
} else {
// call next middleware
next()
}
}
);
})
The code is a bit rough as I don't have a chance to test it right now, but the idea should be clear: wrap all dynamic handlers array in 1 middleware, that will loop through the array. And as you add or remove handlers to the array, only the ones left in the array will be called.
You can use the express-dynamic-middleware to make this.
https://github.com/lanbomo/express-dynamic-middleware
Use it like this
const express = require('express');
// import express-dynamic-middleware
const dynamicMiddleware = require('express-dynamic-middleware');
// create auth middleware
const auth = function(req, res, next) {
if (req.get('Authorization') === 'Basic') {
next();
} else {
res.status(401).end('Unauthorization');
}
};
// create dynamic middleware
const dynamic = dynamicMiddleware.create(auth);
// create express app
const app = express();
// use the dynamic middleware
app.use(dynamic.handle());
// unuse auth middleware
dynamic.unuse(auth);
No way of removing a middleware as far as I know. however, you can assign a boolean flag to 'deactivate' a middleware at anytime you want.
let middlewareA_isActivate = true;
// Your middleware code
function(req, res, next) {
if (!middlewareA_isActivate) next();
// .........
}
// Deactivate middleware
middlewareA_isActivate = false;
EDIT :
After reading through ExpressJs (4.x) code, I notice that you can access the middlewares stack via app._router.stack, manipulation goes from there I guess. Still, I think this 'trick' might not be able to work in future Express
P/s: Not tested how Express behaves when manipulate the middlewares stack directly though
Following from the hints above, I've add success with the following on express 4.x. My use case was logging what was coming in with Slack Bolt, so I could capture and then mock it:
// Define a handy function for re-ordering arrays
Array.prototype.move = function(from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
// Use the normal use mechanism, so that 'extra' stuff can be done
// For example, to log further up the order, use app.use(morgan("combined"))
app.use([my-middleware]);
// Now adjust the position of what I just added forward
const numElements = app._router.stack.length;
app._router.stack.move(numElements - 1, 1);
You can use
console.log("Stack after adjustment", app._router.stack)
to confirm the new order is what you want. (For Slack Bolt, I had to use app.receiver.app because the Bolt app wraps the express app.)
We can write like this.
// route outside middleware
route.get("/list", (req, res)=>{
res.send("from listing route");
});
//use middleware
router.use(Middlewares.AuthMiddleware.isValidToken);
//routes inside the middleware
route.post("/create", (req, res)=>{
res.send("from create route");
});
route.delete("/delete", (req, res)=>{
res.send("from delete route");
});
So basically, write routes before injecting middleware into your route.

How to put middleware in it's own file in Node.js / Express.js

I am new to the whole Node.js thing, so I am still trying to get the hang of how things "connect".
I am trying to use the express-form validation. As per the docs you can do
app.post( '/user', // Route
form( // Form filter and validation middleware
filter("username").trim()
),
// Express request-handler gets filtered and validated data
function(req, res){
if (!req.form.isValid) {
// Handle errors
console.log(req.form.errors);
} else {
// Or, use filtered form data from the form object:
console.log("Username:", req.form.username);
}
}
);
In App.js. However if I put something like app.get('/user', user.index); I can put the controller code in a separate file. I would like to do the same with the validation middleware (or put the validation code in the controller) to make the App.js file easier to overview once I start adding more pages.
Is there a way to accomplish this?
Basically I would like to put something like app.get('/user', validation.user, user.index);
This is how you define your routes:
routes.js:
module.exports = function(app){
app.get("route1", function(req,res){...})
app.get("route2", function(req,res){...})
}
This is how you define your middlewares:
middlewares.js:
module.exports = {
formHandler: function(req, res, next){...}
}
app.js:
// Add your middlewares:
middlewares = require("middlewares");
app.use(middlewares.formHandler);
app.use(middlewares...);
// Initialize your routes:
require("routes")(app)
Another way would be to use your middleware per route:
routes.js:
middlewares = require("middlewares")
module.exports = function(app){
app.get("route1", middlewares.formHandler, function(req,res){...})
app.get("route2", function(req,res){...})
}
I hope I answer your questions.
You can put middleware functions into a separate module in the exact same way as you do for controller functions. It's just an exported function with the appropriate set of parameters.
So if you had a validation.js file, you could add your user validation method as:
exports.user = function (req, res, next) {
... // validate req and call next when done
};

Resources