I have a few middlewares that I want to combine into one middleware. How do I do that?
For example...
// I want to shorten this...
app.use(connect.urlencoded())
app.use(connect.json())
// ...into this:
app.use(combineMiddleware([connect.urlencoded, connect.json]))
// ...without doing this:
app.use(connect.urlencoded()).use(connect.json())
I want it to work dynamically -- I don't want to depend on which middleware I use.
I feel like there's an elegant solution other than a confusing for loop.
Express accepts arrays for app.use if you have a path:
var middleware = [connect.urlencoded(), connect.json()];
app.use('/', middleware)
However, if you want a generic combineMiddleware function, you can build a helper easily without any additional libraries. This basically takes advantage of the fact that next is simply a function which takes an optional error:
/**
* Combine multiple middleware together.
*
* #param {Function[]} mids functions of form:
* function(req, res, next) { ... }
* #return {Function} single combined middleware
*/
function combineMiddleware(mids) {
return mids.reduce(function(a, b) {
return function(req, res, next) {
a(req, res, function(err) {
if (err) {
return next(err);
}
b(req, res, next);
});
};
});
}
If you like fancy stuff, here is one of possible solutions:
var connect = require('connect')
var app = connect()
function compose(middleware) {
return function (req, res, next) {
connect.apply(null, middleware.concat(next.bind(null, null))).call(null, req, res)
}
}
function a (req, res, next) {
console.log('a')
next()
}
function b (req, res, next) {
console.log('b')
next()
}
app.use(compose([a,b]))
app.use(function (req, res) {
res.end('Hello!')
})
app.listen(3000)
Here is what it does: compose function takes array of middleware and return composed middleware. connect itself is basically a middleware composer, so you can create another connect app with middlewares you want: connect.apply(null, middleware). Connect app is itself a middleware, the only problem is that it doesn't have a next() call in the end, so subsequent middleware will be unreachable. To solve that, we need another last middleware, which will call next : connect.apply(null, middleware.concat(last)). As last only calls next we can use next.bind(null, null) instead. Finally, we call resulting function with req and res.
Old question, but the need is still frequent for all the things using middlewares, like connect, express or custom made req/res/next patterns.
This is a very elegant and purely functional solution:
File ./utils/compose-middleware.js:
function compose(middleware) {
if (!middleware.length) {
return function(_req, _res, next) { next(); };
}
var head = middleware[0];
var tail = middleware.slice(1);
return function(req, res, next) {
head(req, res, function(err) {
if (err) return next(err);
compose(tail)(req, res, next);
});
};
}
module.exports = compose;
The final result of the compose(middlewareList) is a single middleware that encapsulates the whole chain of middleware initially provided.
Then simply import it and use like this:
File app.js:
var connect = require('connect');
var compose = require('./utils/compose-middleware');
var middleware = compose([
connect.urlencoded(),
connect.json()
]);
var app = connect();
app.use(middleware);
A simple and native way, and you don't need to install anything.
const {Router} = require('express')
const combinedMiddleware = Router().use([middleware1, middleware2, middleware3])
Then you can use the combinedMiddleware where you want. For example, you may want to run different set of middlewares/handlers for the same route depending on some conditions (a request attributes, for example):
app.get('/some-route', (req, res, next) => {
req.query.someParam === 'someValue'
? combinedMiddleware1(req, res, next)
: combinedMiddleware2(req, res, next)
})
If you're willing to use a library:
https://www.npmjs.org/package/middleware-flow
var series = require('middleware-flow').series;
var app = require('express')();
app.use(series(mw1, mw2, mw2)); // equivalent to app.use(mw1, mw2, mw3);
Make a list and use a loop.
const connect = require('connect')
const { urlencoded, json } = require('body-parser')
const app = connect()
[ urlencoded(), json() ].forEach(app.use, app)
The second argument of .forEach is used for this, but if you like you can also do the same with:
[ urlencoded(), json() ].forEach(app.use.bind(app))
Related
I am wondering if I can pass a connected database instance to an express middleware as an argument.
For example:
// in app.js
const mysql = require('mysql');
const mysqlCon = mysql.createConnection({
// some configurations...
})
mysqlCon.connect((err)=> if(err) throw err);
then in a separate file...
// in a separate file called: login handler
const loginHandler = (req, res, dbCon) => {
// query database and stuff...
}
module.exports = loginHanlder;
then back in app.js passes the established mysql connection into the middleware handler
// back in app.js
// a route
const loginHandler = require('./handlers/loginHanlder.js');
app.get('/login', loginHanlder(req, res, mysqlCon));
Will this work? Or are there a conventionally better way of achieving this goal?
The way you're trying to do it won't work, as req,res will be undefined.
However, you could create a factory function that takes the connection as a parameter and retours a route-handler function. Since the function that will be returned is a closure, it will have access to the db-connection in the parent scope. Something like:
function getRequestHandler(dbConn) {
return function (req, res, next) {
// use dbConn here to do stuff
}
}
You can use it like this:
const dbConn = initConnection(); // connect to your db here and return the connection instance
app.get('/login', getRequestHandler(dbConn));
Alternatively, if you don't want/need to create your own factory function, you can simply do:
app.get('/login', (req, res) => loginHandler(req, res, mysqlCon));
I have a server in express and it uses an external api. I would like for each request to that api ('/api/*'), that it appends a query param in the url without to write it for each requests.
app.use(function(req, res) {
req.query.key = process.env.APIKEY;
});
I tried something like that but it doesn't work.
I thought of doing something like :
app.get('/api/stuff', addApiKey, api.stuff);
Is there a better way?
You need to supply your middleware function with a next callback:
function addApiKey(req, res, next) {
req.query.key = process.env.APIKEY;
next();
});
app.get('/api/:endpoint', addApiKey, function(req, res) {
// do your stuff here
});
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.
I'm using Node.js with express and already know the existence of response.redirect().
However, I'm looking for more of a forward() functionality similar to java that takes the same parameters as redirect, but internally forwards the request instead of having the client perform the redirect.
To clarify, I am not doing a proxy to a different server. I'd like to forward('/other/path') directly within the same app instance
It wasn't apparently obvious how to do this from the express documentation. Any help?
You just need to invoke the corresponding route handler function.
Option 1: route multiple paths to the same handler function
function getDogs(req, res, next) {
//...
}}
app.get('/dogs', getDogs);
app.get('/canines', getDogs);
Option 2: Invoke a separate handler function manually/conditionally
app.get('/canines', function (req, res, next) {
if (something) {
//process one way
} else {
//do a manual "forward"
getDogs(req, res, next);
}
});
Option 3: call next('route')
If you carefully order your router patterns, you can call next('route'), which may achieve what you want. It basically says to express 'keep moving on down the router pattern list', instead of a call to next(), which says to express 'move down the middleware list (past the router)`.
You can implement forward (aka rewrite) functionality by changing request url property and calling next('route').
Note that the handler performing forward needs to be configured before other routes which you perform forwards to.
This is example of forwarding all *.html documents to routes without .html extension (suffix).
function forwards(req, res, next) {
if (/(?:.+?)\.html$/.test(req.url)) {
req.url = req.url.replace(/\.html$/, '');
}
next('route');
}
You call next('route') as the last operation. The next('route') passes control to subsequent routes.
As mentioned above, you need to configure forwards handler as one of the first handlers.
app.get('*', forwards);
// ...
app.get('/someroute', handler);
The above example will return the same content for /someroute as well as /someroute.html. You could also provide an object with a set of forward rules ({ '/path1': '/newpath1', '/path2': '/newpath2' }) and use them in forward mechanism.
Note that regular expression used in forwards function is simplified for mechanism presentation purposes. You would need to extend it (or perform check on req.path) if you would like to use querystring parameters etc.
I hope that will help.
For Express 4+
Using the next function does not work if the next handler is not added in the right order. Instead of using next, I use the router to register the handlers and call
app.get("/a/path", function(req, res){
req.url = "/another/path";
app.handle(req, res);
}
Or for HTML5 mode of React/Angular
const dir = process.env.DIR || './build';
// Configure http server
let app = express();
app.use('/', express.static(dir));
// This route sends a 404 when looking for a missing file (ie a URL with a dot in it)
app.all('/*\.*', function (req, res) {
res.status(404).send('404 Not found');
});
// This route deals enables HTML5Mode by forwarding "missing" links to the index.html
app.all('/**', function (req, res) {
req.url = 'index.html';
app.handle(req, res);
});
Using the next function does not work if the next handler is not added in the right order. Instead of using next, I use the router to register the handlers and call
router.get("/a/path", function(req, res){
req.url = "/another/path";
router.handle(req, res);
}
Express 4+ with nested routers
Instead of having to use the outside of route/function app, you can use req.app.handle
"use strict";
const express = require("express");
const app = express();
//
// Nested Router 1
//
const routerOne = express.Router();
// /one/base
routerOne.get("/base", function (req, res, next) {
res.send("/one/base");
});
// This routes to same router (uses same req.baseUrl)
// /one/redirect-within-router -> /one/base
routerOne.get("/redirect-within-router", function (req, res, next) {
req.url = "/base";
next();
});
// This routes to same router (uses same req.baseUrl)
// /one/redirect-not-found -> /one/two/base (404: Not Found)
routerOne.get("/redirect-not-found", function (req, res, next) {
req.url = "/two/base";
next();
});
// Using the full URL
// /one/redirect-within-app -> /two/base
routerOne.get("/redirect-within-app", function (req, res, next) {
req.url = "/two/base";
// same as req.url = "/one/base";
//req.url = req.baseUrl + "/base";
req.app.handle(req, res);
});
// Using the full URL
// /one/redirect-app-base -> /base
routerOne.get("/redirect-app-base", function (req, res, next) {
req.url = "/base";
req.app.handle(req, res);
});
//
// Nested Router 2
//
const routerTwo = express.Router();
// /two/base
routerTwo.get("/base", function (req, res, next) {
res.send("/two/base");
});
// /base
app.get("/base", function (req, res, next) {
res.send("/base");
});
//
// Mount Routers
//
app.use("/one/", routerOne);
app.use("/two/", routerTwo);
// 404: Not found
app.all("*", function (req, res, next) {
res.status(404).send("404: Not Found");
});
app.get('/menzi', function (req, res, next) {
console.log('menzi2');
req.url = '/menzi/html/menzi.html';
// res.redirect('/menzi/html/menzi.html');
next();
});
This is my code:when user enter "/menzi",the server will give the page /menzi/html/menzi.html to user, but the url in the browser will not change;
You can use run-middleware module exactly for that. Just run the handler you want by using the URL & method & data.
https://www.npmjs.com/package/run-middleware
For example:
app.runMiddleware('/get-user/20',function(code,body,headers){
res.status(code).send(body)
})
I have very simple node.js noob question. How do I pass a variable to an exported route function?
Routes file
exports.gettop = function(n, req, res) {
console.log(n);
res.send(200);
};
Server file
app.get('/api/v1/top100', routes.gettop(100));
Error: .get() requires callback functions but got a [object Undefined]
For your example, you want to create a new function that will close around your value of n. In your case, you are executing gettop and passing the returned value to express as your route, which means gettop needs to return the route handler.
exports.gettop = function(n){
return function(req, res) {
console.log(n);
res.send(200);
};
};
As your code looks like you're using express you can use express app locals and express result locals to pass variables to your route. While the other answers propose working solutions I think that it's less obtrusive to use express mechanisms to set these variables.
With response locals (See Express API reference) you first have to set a variable somewhere in a middleware or route. I'll show the middleware approach
app.use(function(req,res, next) {
res.locals.top = 200;
next();
});
then in your route you can access this property via res.locals.variablename
exports.gettop = function(req, res) {
console.log(res.locals.top);
res.send(200);
};
In case you want to make these settings application wide a better approach is to use app locals (See Express API reference)
To set an app locals variable you can use
app.locals.top = 100;
To access this variable from your route use
exports.gettop = function(req, res){
console.log(req.app.locals.top);
res.send(200);
};
As an alternative to loganfsmyth's (very valid!) solution, you could leave your gettop function as-is and create a partial function:
app.get('/api/v1/top100', routes.gettop.bind(null, 100));
Try
app.post('/find_user',
require('./naas/autentication'),
require('./naas/authorization')(paramForRouter),
require('./routes/users'));
Where
require('./naas/autentication') is for example
module.exports = function (req, res, next) {next();}
And require('./naas/authorization')(paramForRouter) is
module.exports = function (paramForRouter) {
return function (req, res, next) {
this.param = paramForRouter;
console.log("Param value",this.param);
next();
};
};