Brad Oyler
Lead Developer, NBCNews.com
@bradoyler
bradoyler@gmail.com
~
Building content sites with
preview.nbcnews.com
the stack
Express.js
+
Handlebars.js
+
Cache service
+
Redis
MODULES
"dependencies": {
"async": "0.9.0",
"compression": "1.0.2",
"cron": "1.0.4",
"eidetic": "0.1.1",
"express": "4.0.0",
"express-handlebars": "0.5.0",
"moment": "2.6.0",
"redis-url": "0.2.0",
"request": "2.34.0",
"route-cache": "0.1.0"
}
*use a CDN for static files
ExPress setup
// npm packages
var express = require('express');
var compression = require('compression');
var routeCache = require('route-cache');
var expressHbs = require('express-handlebars');
// app code
var helpers = require('./helpers');
var routes = require('./routes');
var app = express();
route-cache
module.exports.cacheSeconds = function(ttl) {
return function(req, res, next) {
var cache = cachestore.get(req.path);
if (cache) {
res.send(cache);
} else {
var send = res.send;
res.send = function(string) {
var body = string instanceof Buffer ? string.toString() : string;
cachestore.put(req.url, body, ttl);
send.call(this, body);
};
next();
}
}
};
setup RouteR
app.use('/', require('./routes'));
app.use('/section', require('./routes/section'));
// & handle errors
app.use(function handle404(req, res) { res.status(404); res.render('error', {layout: false}); }); app.use(function handle500(error, req, res, next) { res.status(500); log.error('500 error: ' + error.message + '; stack: ' + error.stack); res.render('error', {layout: false}); });
index route
var cacheService = require('../CacheService');
var router = express.Router();
router.get('/', (req, res, next) {
cacheService.get('coverstories', ttl, function(model) {
res.render('index', model, errorHandler);
});
};
module.exports = router;
Remember 'route-cache'?
cache-service
var localcache = require('memory-cache');
var redis = require('redis');
exports.get = function (key, expires, callback) {
var cachedData = localcache.get(key);
if (cachedData) {
callback(cachedData);
return;
}
redis.get(key, function(error, value) {
localcache.put(key, value, expires);
callback(value);
});
};
use local + remote cache (redis/memcache)
A worker process updates redis
A worker process updates redis
view Engine
app.engine('.hbs', expressHbs({
defaultLayout: 'default',
extname: '.hbs',
layoutsDir: __dirname + '/views/layouts',
partialsDir: [__dirname + '/views/partials'],
helpers: hbsHelpers
}));
A: Familiar syntax, partials & helpers
HBS helpers
module.exports = function() {
return {
ifindexby: this.ifindexby,
formatdate: this.formatdate,
decodeuri: this.decodeuri,
link: this.linkHelper,
'if-equal': this.ifEqual,
imgurl: this.imgurl,
'if-contains': this.ifContains,
articleurl: this.articleurl,
routeurl: this.routeurl,
coverheadline: this.coverheadline,
plusone: this.plusone
};
};
hoping to publish a npm packge soon
{{if-equal}}
exports.ifEqual = function(val, test, options) {
var arrTest = test.split('||');
for (var i = 0; i < arrTest.length; i++) {
if (val === arrTest[i]) {
return options.fn(this);
}
}
return options.inverse(this);
};
{{#if-equal type 'photo'}}
{{> article_photo}}
{{/if-equal}}
{{#if-equal type 'post||externallink'}}
{{> article_post}}
{{/if-equal}}
true, these aren't logic-less templates, but so easy to work with{{imgurl}}
function imgurl(url, width, height) {
imgurl = imgurl.toLowerCase().replace('/i/','/j/')
.replace('.jpg', '.nbcnews-fp-' + width + '-' + height + '.jpg')
.replace('.jpeg', '.nbcnews-fp-' + width + '-' + height + '.jpeg')
.replace('.png', '.nbcnews-fp-' + width + '-' + height + '.png');
return imgurl;
};
{{#each gallery.photos}}
<div class="img_hero img_gallery">
<img class="lazy-lg" data-original="{{imgurl url '1440' '600'}}"/>
<img class="lazy-sm" data-original="{{imgurl url '320' '240"/>
</div>
{{/each}}
Why Content sites?
it's the right tool...
and now...
(data science)
benchmarking
apachebench test
ab -n 200 -c 4 http://localhost:9001/
// 1. express(‘view cache’, false) & !routecache
Requests per second: 12.88 [#/sec] (mean)
// 2. express(‘view cache’, true) & !routecache
Requests per second: 21.38 [#/sec] (mean)
// 3. express(‘view cache’, true) & routecache(15)
Requests per second: 530.56 [#/sec] (mean)
WebPagetest
test results
shameless plug. Download SnapCard
QUESTIONS?
nodejs content sites
By bradoyler
nodejs content sites
One way to build content sites using Node.js, Express and server-side Handlebars templates
- 11,369