On my free time, I recently started to think about the next version of my photography portfolio which starts to be old and especially is not designed to be effectively browsable on tablets.
And, there are also other things I'd like to fix...
Anyway, as I recently got some basic skills in NodeJS, I challenged myself to rewrite my dear portfolio with this technology. I looked for an interesting templating engine not too far from Twig which I like a lot then I chose to go with Nunjucks, which is very similar and seems not to be dead.
In my current portfolio, I use Silex which is built on the top of a DIC. The Twig Service Provider has an excellent interesting idea of injecting the whole Silex application in a Twig global variable.
This was very convenient when, from the template, you need to access to the HTTP Request params for example. Or if you need to access to a service, etc.
I personally rather choose to inject the services/data I need than exposing all of them.
Nunjucks also has the ability to add global variables, services, etc. but when it came to reach to the Express request, I started to rack my brains.
I was hoping Nunjucks could do that but as the Express integration is more sort of a helper than a real extension of the template engine, the answer was no.
I tried to play with it by setting up examples but it did not work and after a dull search on the Internet, I found what I wanted. Even I wish it was more elegant.
I wrote this article to share a more elegant way (well, I believe it is...) to inject Express content as Nunjucks global variables.
Project directory tree
.
├── README.md
├── app
│ ├── config.js
│ ├── index.js
│ ├── middleware
│ │ ├── error-handler.js
│ │ └── inject-globals.js
│ ├── route
│ │ └── index.js
│ ├── service
│ │ ├── TemplateEngine.js
│ │ └── index.js
│ └── view
│ ├── front
│ ├── macro.njk
│ └── skeleton.njk
├── env
├── package.json
├── public
└── server.js
I have the server and the application bootstrap in server.js
, that's what npm start
will run. My application kernel is in app/index.js
while its configuration lays in app/config.js
.
The rest is pretty much standard, I guess it evolves from project to project. The organization of a NodeJS project is still something that might evolve over time :).
Nunjucks integration
The files that arouse interest are:
app/service/TemplateEngine.js
app/middleware/inject-globals.js
app/index.js
I chose to create a badly-named template engine service: TemplateEngine.js
:
var nunjucks = require('nunjucks');
var config = require(__dirname + '/../config').template;
module.exports.init = function (app) {
var env = nunjucks.configure(config.path, {
throwOnUndefined: config.strict,
autoescape: true,
watch: config.watch,
noCache: config.cache,
express: app
})
.addFilter('json', JSON.stringify);
// Sets Nunjucks as the Express template engine
app.set('engine', env);
};
Here you configure your template engine and I believe it would be the best place to append a bunch of filters as well.
Into the application kernel (app/index.js
), I set up my services and routes:
var service = require(__dirname + '/service');
module.exports = function (app) {
app.use(require('body-parser').json());
// Registers template engine
service.TemplateEngine.init(app);
// Injects global variables into the template engine
app.use(require(__dirname + '/middleware/inject-globals'));
// Application routes
require(__dirname + '/route')(app);
// Application error handler
app.use(require(__dirname + '/middleware/error-handler'));
};
Here I briefly configure my Express framework then I register the template engine. As I wrote earlier, I might re-work the interface/naming of the template engine service.
Anyway, right after I declare a catch-all middleware in which I will append Nunjucks Express variables.
Here's the content of app/middleware/inject-globals.js
:
module.exports = function (req, res, next) {
var engine = res.app.get('engine');
var config = req.app.get('config');
engine.addGlobal('config', config);
engine.addGlobal('request', req);
next();
};
As simple as it looks like. I chose here to inject the incoming request and my application configuration.
In your template file, you can have access at your request bag, such as headers for example:
<code>{{ request.headers | json(null, 4) }}</code
Pretty cool no?
Now you know how to inject Express request or any other data you need into your template files without having to manually inject at each single rendering call.
I believe there plenty things to improve in all aspects of what I've shown above but I'm no expert so if you have feedback, please share!
Thank you!