Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Node.js and Express 3: Set app.locals that are loaded asynchrously

Node.js and Express 3: Set app.locals that are loaded asynchrously

Problem

I am using Node.js and Express 3.0, and I'm pretty clear on the difference between app.locals and res.locals...

I'd like to set some app.locals variables ('newest' and 'popular') that I want to reference globally in any of my EJS templates. The data for these variables come from a Database (CouchDB/Cradle) and is also accessible via one of my app.routes (ie: /myappdata) which simply does as res.json to expose my application data from database.

So what's the best approach for loading this stuff once into my app.locals? Should I create a middleware function with app.use() before my router. How can I ensure that the database doesn't get called on every request and only when my app starts? How should I do async callback in app.use()?

I've also tried directly setting app.locals(), but it seems that 'newest' and 'popular' vars are "undefined" to my templates at certain times. (Maybe something is stepping on the app.locals??)

Here is my 'app.js' returned to server.js on startup:

var express = require('express'),
    engine = require('ejs-locals'),
    conf = require('./conf'),
    cradle = require('cradle').(conf.databaseConn),
    app = express();

exports.init = function(port) {

    app.locals({
        _layoutFile:'layout.ejs',
        newest:{}, // like to set this from db once
        popular:{} // like to set this from db once
    });

    app.configure(function(){
        app.set('views', __dirname + '/views');
        app.set('view engine', 'ejs');

        app.use(express.compress());

        app.use(express.static(__dirname + '/static'));

        app.use(express.bodyParser());
        app.use(express.cookieParser());
        app.use(express.cookieSession({cookie:{path:'/',httpOnly:true,maxAge:null},secret:'blh'}));

        app.use(function(req, res, next){
            res.locals.path = req.path;
            res.locals.user = req.session.user;
            next();
        });

        app.use(app.router);
    });

    app.engine('ejs', engine);

    app.configure('development', function(){
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
    });

    app.configure('production', function(){
        app.use(express.errorHandler()); 
    })

    app.use(function(err, req, res, next){
        res.render('500.ejs', { locals: { error: err, path:"" },status: 500 }); 
    });

    var server = app.listen(port);
    console.log("Listening on port %d in %s mode", server.address().port, app.settings.env);
    return app;

}

Thanks for any suggestions.

Problem courtesy of: ZimSystem

Solution

You could create a middleware which checks if app.locals.newest (/.popular) is defined; if so, just call next() immediately; if not, perform the database query, store the result in app.locals, and call next() (that's how you create an asynchronous middleware). If implemented that way, the queries will be performed during the first (and only the first) request.

An alternative would be to perform the queries at startup time, before you call app.listen(). That way, you're sure the variables are loaded when the server starts listening for requests.

To coordinate multiple asynchronous database queries, you could use a module like async or queue.

Solution courtesy of: robertklep

Discussion

View additional discussion.



This post first appeared on Node.js Recipes, please read the originial post: here

Share the post

Node.js and Express 3: Set app.locals that are loaded asynchrously

×

Subscribe to Node.js Recipes

Get updates delivered right to your inbox!

Thank you for your subscription

×