Better middleware chaining in Express - javascript

This is my Express configuration code:
var server = express()
.use(express.cookieParser())
.use(express.session({secret: buffer.toString('hex')}))
.use(express.bodyParser())
.use(express.static('./../'));
server.use(server.router);
It's quite annoying that the last .use is not chained like the rest.
How can I do full chaining of my Express middleware definitions?

The express use implementation returns this every time and is thus always chainable. The chainability problem comes from your unnecessary use of server.use(server.router), which is automatically done for you by express when your first non-middleware route is added via server.get, server.post, etc. This can can be seen in the express source code here, although this is a common point of confusion and problems (most of which come from doing server.user(server.router) at all as opposed to just letting the router go where it goes by default, which is almost always the correct place for it. So here's the forumla:
Properly ordered server.use calls to set up your middleware
Your specific routes with server.get('/home') and the other HTTP method functions
Any error handling, 404 middleware, etc
Express will add the router after 1 but before 2, and all will be well.
In the comments you claim if you omit the server.router line, you lose access to req.session. Sorry, but this is simply not the case. Here's an entire working express app that shows a middleware immediately before the session middleware having req.session undefined, and another one immediately after the session middleware having it exist.
var express = require('express');
var server = express();
server.use(function (q,r,n) {console.log('Before cookie:', q.session);n();});
server.use(express.cookieParser());
server.use(function (q,r,n) {console.log('after cookie:', q.session);n();});
server.use(express.session({secret: 'SEKRET'}));
server.use(function (q,r,n) {console.log('after session:', q.session);n();});
server.get('/', function (q,r,n) {r.send("you got slashed");});
server.listen(3000);
Run it and you see this on the console.
Before cookie: undefined
after cookie: undefined
after session: { cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
However, if you are asking why you can't use the server variable as a function parameter in the same statement in which it is defined, that's just how JavaScript works and has zero to do with express. You can't do this:
var foo = console.log(foo.bar);
Because foo is undefined until that entire statement is complete.
FWIW my opinion is that chainable code like this is dubious. It is unfriendly to debuggers and breakpoints, not supported as a first-class language feature, and isn't really saving you any characters compared to each line being unindented and app.use(app.router), but so be it.

Related

Is it possible to use the capture groups from the regex of an express route?

I'm working on a fairly large node/express app using the GOV.UK Prototyping Kit. I have separated multiple instances of the prototype into standalone versions for archival purposes e.g. this isn't a build it once 'DRY' approach but rather there's quite a bit of duplication as the prototypes evolve (sometimes significantly). The crucial thing is that each version e.g. /phase/release/ is suitably separated.
I've separated the routes quite nicely but the final thing that would help would be to have a dynamic route.use -> require so that I don't have to manually update the top-level routes.js file for each version release.
I use regex matching in my routes quite a lot so I'm fairly familiar with that, but what I'm wondering is how I can use the details of the match to dynamically build the require. Or perhaps use something in req.params - but I don't think that's available outside of a route.
Here's an outline of my code. The static route (R1) works fine but neither of the dynamic routes (R2/R3) work (when uncommented):
const express = require('express')
const router = express.Router()
router.use('/:phase/:release', function (req, res, next) {
// Make phase and release available in subsequent routes
let prototype = {}
prototype.phase = req.params.phase
prototype.release = req.params.release
req.session.data.prototype = prototype
next()
})
// Phase/release specific routes
// -----------------------------
// R1. This works
router.use('/test/a/', require('./views/test/a/_routes.js'));
// R2. But a dynamic version world be better?...
// router.use(/(alpha|beta|test)\/([abc])\//, require('./views/' + \1 + '/' + \2 '/_routes.js'));
// R3. ...or...
// router.use('/:phase/:release/', require('./views/' + req.params.phase + '/' + req.params.release '/_routes.js'));
Ultimately I think my approach is wrong and that it's not possible to reference the regex capture groups this way, but equally I don't think I can access the req object (and therefore req.params) as part of the require path.
I would expect there to be a way to use information known prior to making the require within the constructed require path but I just can't work out how.
Any input/help much appreciated.
Hope i understand your question correctly.
If the _routes.js exports a router which is essentialy a function which accepts req, res, next params then you can do this:
router.use('/:phase/:release/', function(req, res, next){
var anotherRouter = require('./views/' + req.params.phase + '/' + req.params.release '/_routes.js');
anotherRouter(req, res, next);
});
Express documentation for router.get documents in the last exemple that when you use a regex for the route parameter, the captured groups are available in req.params by position (tested on Express 4.17). This also works with a required router handler instead of a function (as you do), of course.
router.use(/(alpha|beta|test)\/([abc])\//, (req, res) => {
console(req.params[0]); // will show the first captured group: alpha, beta or test
console(req.params[1]); // will show the 2nd captured group
});

Odd behavior of fs.readdirSync

I use nodejs 5.5.0, and pm2 1.0.1. I run the script with:
npm start
And in package.json, it is defined as:
"scripts": {
"start": "pm2 start bin/www --watch"
},
I have this simple script that returns a list of videos and it works:
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
const fs = require('fs');
shows = fs.readdirSync(`public/videos`);
res.render('index', shows);
});
module.exports = router;
But I observed something interesting... If I make the below modification, it no longer works, and returns an empty list:
//shows = fs.readdirSync(`public/videos`);
res.render('index', fs.readdirSync(`public/videos`));
To make it more interesting, the following works:
shows = fs.readdirSync(`public/videos`);
res.render('index', fs.readdirSync(`public/videos`));
It almost seems to me, if there is some sort of delay or caching, fs.readdirSync works fine, but not if it is called straight on the same line.
Did I miss something simple?
Somebody correct me if I'm wrong..
But I believe it is because res.render has a third argument that you don't see which is callback noted here
So when you create the variable in your first example, you block until you get the variable, and then your render the template with the variables available at run time.
But in your second example, you're passing in that option, but res.render's callback is calling before readdirSync can finish populating.
I figured it out and it was actually something really simple that I missed. The name of the local variable is significant.
In case 3, although shows is not explicitly passed in, it is actually visible to the page being rendered using swig.
If I do not want to introduce the variable shows, I could have done the following and it works:
res.render('index', {shows: fs.readdirSync('public/videos')});

Express - better pattern for passing data between middleware functions

I just brought up this issue with Express and I am interested in what StackOverflow thinks about it:
https://github.com/strongloop/express/issues/2831
my question is why Express opts to disallow the developer from passing data directly between middleware functions and basically forces you to assign temporary data to the request object in what I have always considered to be a quite awkward assignment.
to be more specific:
passing data between middleware functions usually involves doing this
req.specialData = {}
next();
however, it would be perhaps much easier and more performant (!) if this were possible
next(null,data);
or
function mySpecialMiddleWare(req,res,next,data){}
//now, call the above function
mySpecialMiddleWare(req,res,next, {some:data});
the problem is that Express uses a, what I believe to be, stupid way of determining if the call is to a normal middleware function or the next error first middleware function, by checking to see if the function.length > 3 or function.length === 4...
is there any chance what I am saying makes any sense?
wouldn't it be better/easier/faster/stronger to allow direct passing of data via middleware functions instead of assigning data awkwardly to req ??
perhaps Express already has this capability and I am just misinformed?
my question is why Express opts to disallow the developer from passing data directly between middleware functions and basically forces you to assign temporary data to the request object in what I have always considered to be a quite awkward assignment.
So I think the API is the way it is to encourage most middleware to be modular, re-usable, and loosely coupled. That means the generally should do something without being concerned too much about what other middleware might run before them or after them. In order to achieve this and create an ecosystem of loosely-compatible middleware functions, express keeps the API fairly generic. There are pros and cons to this. But as a direct answer to your first question, I would say to keep the interface simple, consistent, and flexible as well as try to avoid strict ordering dependencies.
As in your case, there might be implicit dependencies between middlewares. Another common example would be typically your session middleware has an implicit dependency that your cookie middleware runs before it. Having these all be implicit can be argued to create bugs and unnecessary troubleshooting. On the other hand, it enables application developers to more easily mix and match middlewares that might otherwise have no awareness of one another.
In hindsight I think both I as well as some of the express maintainers would agree that using function arity (number of expected arguments) for semantic API reasons was an odd and poor choice on TJ's part. I think if the project were to be rewritten a more explicit API would be defined for error handling.
wouldn't it be better/easier/faster/stronger to allow direct passing of data via middleware functions instead of assigning data awkwardly to req ??
Better - this is highly arguable and opinion based. There is a lot to be said for the simplicity of it and the evidence is the huge ecosystem and usage. There are alternatives available such as hapi, restify, etc, though so you might consider them.
Easier - probably not. What's there is pretty easy.
Faster - probably not in any meaningful way. Not sure why you think your version would be faster, but best to bring metrics when you make such claims.
Stronger - If by "stronger" you mean more explicit that's probably true but there's a reason some people still prefer JavaScript even though TypeScript all the way up through Haskell exist and are definitely "stronger" in some sense of the word.
There might be advantages to what Express is doing, however, it is worth noting that Express allows passing data between middlewares using res.locals. With that said, your question inspired me to make this library. Expressjs-plus.
With it, you will be able to pass data between your middlewares easily without having to use the request nor the response object.
You can use regular javascript functions e.g. function(varInResLocals, varInReqParams, callback) instead of express middleware signature.
Here is a working example for you.
var express = require('express');
var ExpressPlus = require('expressjs-plus').ExpressPlus;
var app = express();
// simple handler example
var userHandler = function(param, paramsArray, req, res){
if(param !== 'user') return false;
paramsArray.push("USER WAS FOUND!");
return true;
};
// this handler allows you to pass res.locals properties between your middlewares seemingly,
// it the parameter was found in locals, it attaches it to paramsArray.
var resLocalsHandler = function(param, paramsArray, req, res){
if(param in res.locals){
paramsArray.push(res.locals[param]);
return true;
}else return false;
};
var appPlus = new ExpressPlus(app, [userHandler, resLocalsHandler], []);
var regularFunction = function(user, id, cb){
return cb(null, { response: {user: user, id: id}, status: 200, resLocalsVar: "passVar" });
};
// resLocalsVar was passed in a previous method
var regularFunction2 = function(resLocalsVar, user, id, cb){
// now you can have access to it
console.log(resLocalsVar);
return cb(null);
};
// the responder at the end will use res.locals.status and res.locals.response to issue an HTTP response
app.use(appPlus.GMV(regularFunction), appPlus.GMV(regularFunction2), appPlus.responder);
// adds error handlers, it will add a default error handler along with the list of error handlers passed
// in this case, no error handlers were passed
appPlus.setErrorHandlers();
app.listen(3001, function(){
console.log('Listening!');
});

Node.js structure and flow of the application

I've just completed writing my first Node application. It's working well but as I read more from the community and absorb details I've come to the conclusion that my app is a bit different from the examples I'm reading.
So I dug deeper into finding solutions and reading a lot of howtonode.org. I stumbled on the concept of control-flow, promises and the Q-library. I'm starting to digest it all, I think I get it but I wanted to confirm my research and findings. Before I decide to restructure everything with Qlibrary I'm wondering if I've at least got my core app and methodology down pat, it will basically delegate how I move forward at this point.
app.js
// Require global libraries
var app = {
config : require('../../config.json'), // my custom config files
restify : require('restify'),
path : require('path'),
mongo : require('mongodb'),
model : require('./models.js'),
q : require('q'),
api : require('./api_util.js'),
underscore : require('underscore')
};
app.server.pre(function (request, response, next) {
request.app = app;
return next();
});
app.server = app.restify.createServer(app.config.api);
require('controller.js')(app);
app.server.listen(app.config.api.port);
controller.js
module.exports = function Controller(app, parcel, controller_name) {
app.server.get({
path : '/my/controller/',
version : '1.0.0',
name : 'My REST GET Controller'
}, function(request, response, next){
var model = new request.app.model();
// do some other stuff
));
}
I've taken the approach of encapsulating all my libraries and important components in the app variables and pass it into my controller so that I can call the references directly and only pass in one variable between each callback.
For every request after that I'm accessing the app variable from the request value that I attached to the pre in app.js (hence the request.app.model() function within the route callback function.
I'm wondering if I should be concerned about structuring my code this way. Its made it very easy to pass the reference back and forth without having to worry about global variables or any of that stuff. I read something slightly similar where someone hinted that this is a smart direction because it really helps with namespacing and getting around the whole global variable declaration issue (which is a big no-no).
If I was pushed in the right direction I think Q library and everything else will fall into place rather nicely.
Thanks!

Variable HTTP/HTTPS Express Server

I'm sure this is just a simple Javascript scoping issue (go figure), but I've tried several different ways and can't seem to get this to work.
The idea is that the Express.js server defaults to HTTP with the call to express.createServer(), but optionally switches to HTTPS upon detection of ./conf/cert.pem and ./conf/key.pem. I'm using asynchronous calls to Node's path.exists(), whereby the second callback argument contains the boolean result.
I have them nested currently so that a HTTPS server isn't created until it's "safe" (we know the outcome of whether the two files exist or not) since no ordering is guaranteed otherwise. However, as I mentioned, I've tried several different ways, but I can't seem to modify the outer app variable.
I'm sure there's a simple fix that I'm not seeing right now, but any help would be appreciated!
app = module.exports = express.createServer();
path.exists('./conf/key.pem', function(exists){
var keyExists = exists;
path.exists('./conf/cert.pem', function(exists) {
var certExists = exists;
if (keyExists && certExists) {
app = express.createServer({
key: fs.readFileSync('./conf/key.pem'),
cert: fs.readFileSync('./conf/cert.pem')
});
}
});
});
This is not ideal. You shouldn't create a regular http server only to have it overwritten by an https server. I think the problem you're referring to comes from the fact that you're setting the app variable below, but not module.exports. So module.exports still refers to the original server created. This is what you should do:
var app = express.createServer({
key: fs.readFileSync('./conf/key.pem'),
cert: fs.readFileSync('./conf/cert.pem')
});
module.exports = app;
There's no reason to check whether the files exist first. If the files don't exist, readFileSync will just throw an ENOENT. There is also no reason to do anything asynchronously before you have even entered the event loop.
I don't know why you would want your server to conditionally be https, but to do something similiar to what you were trying to do there:
var app;
try {
app = express.createServer({
key: fs.readFileSync('./conf/key.pem'),
cert: fs.readFileSync('./conf/cert.pem')
});
} catch(e) {
if (e.code !== 'ENOENT') throw e;
app = express.createServer();
}
module.exports = app;
Or, this might look nicer:
var app;
if (path.existsSync('./conf/key.pem')) {
app = express.createServer({
key: fs.readFileSync('./conf/key.pem'),
cert: fs.readFileSync('./conf/cert.pem')
});
} else {
app = express.createServer();
}
module.exports = app;
Remember, doing things synchronously is fine as long as you don't have to serve a million requests at the same time.

Categories

Resources