Passing variables through Express middleware

Almost in all of my projects, I have a need to store some application based variables. In some cases, I change them on each request (for example – counters). In some cases, they are not changed or changed infrequently. I want to be able to access them in almost any place of my application. I’m also using request based variables – they are tied to the particular request and destroyed once the response is ready.

Application variables

There are at least two ways to store application variables. To store configuration or other variables which are not used in the layout, I’m using app.set this way (the code is placed in the app.js file):

app.set('requestsCounter', 0);

As you can see, now I have requests counter initialized and set to zero. I will now update it on each request start event in the middleware which is connected to each request – in my app.js I have:

//...
var tools = require('./modules/tools');
//...
app.use(tools.onRequestStart);
//...

And in my modules/tools.js file I have this function in place:

//...
exports.onRequestStart = function(req, res, next) {
    var reqCounter = req.app.get('requestsCounter');
    req.app.set('requestsCounter', ++reqCounter);

    next();
}
//...

As you can see, I’m retrieving application variable using req.app.get (app object is passed through req) and I’m storing it again once incremented. This is a silly example but you can get an idea. On the other hand, to store variables which I want to use on the layout, I’m using app.locals – take a look at the examples below, because res.locals are almost similar.

Request variables

I’m storing request based variables in the res object. To be precise – in res.locals. Don’t be confused – res is the response object. This is the place designed to handle everything that should be passed through the middleware and destroyed once the request ends. For example, I’m storing my menu which is generated in tools module in the res.locals.menuItems. Here is part of my code from the modules/tools.js file:

//...
exports.generateUserMenu = function(req, res, next) {
    var menuItems = [{
            label: 'Home',
            href: '/'
        }, {
            label: 'My profile',
            href: '/user'
        }
    ]
    adjustMenuClass(menuItems, req._parsedUrl.pathname);
    res.locals.menuItems = menuItems;
    next();
};
//...

I also added one important line to my app.js file:

hbs.localsAsTemplateData(app);

This way I enabled usage of app.locals and res.locals in the handlebars layouts and templates. As you probably noticed, I’m using layout.hbs as my default layout, but for user area, I want to use the different file. Layout in handlebars can be simply chosen by sending layout variable to the render, or by setting res.locals.layout variable like so:

//...
app.use('/user', function(req, res, next) {
    res.locals.layout = 'layout_user';
    next();
});
//...

This is again the piece of code from app.js – as you can see, I’m simply setting the variable which will point handlebars to the layout_user.hbs file.

Retrieving application and request variables in handlebars

For the layout change, there is nothing more to do but setting proper res.locals.layout variable. All the magic is handled by the handlebars. When talking of the other value stored in locals, you can take a look at this example:

<ul>
    {{#each menuItems}}
        <li class="{{ class }}">
            <a href="{{ href }}">{{ label }}</a>
            {{#if menuItems}}
                {{> menuList}}
            {{/if}}
        </li>
    {{/each}}
</ul>

This piece comes from one of my partials (smaller pieces of handlebars templates) named menuList.hbs. As you can see, the variable of res.locals.menuItems is here used directly, no need to use res.locals prefix. The same will go with app.locals variables. This way you can set various variables in various pieces of your middleware and use them easy on your templates.

Here you can find more about handlebars basics in Express.

You can find this project source on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *