A simple guide for getting Fastify playing nicely with Handlebars as a templating engine. I couldn’t find any support or documentation on this online and therefore it took me a lot longer than I originally expected to get this solution working.
Why?
Some of you might be wondering why I’m wanting to use Fastify instead of express, Express is well known in the community, very well supported and has been proven to be able to handle a decent amount of load in production for a given website or API.
Well yes Express is awesome and I’ve personally used it for several years. However, when you really want to focus on website perfromance and speed that’s when you want to investigate and see what’s out there.
I won’t cover my performance comparison as part of this post but I can safey say on my APIs I have seen around a 80% increase in terms of RPS (Request Per Second) and around a 40% incerate in otheral request speed which is pretty impressive if you as me!!
Notes
Where’s some useful tips that tripped me up massively when I was migrating my handlebars layouts and particals over.
- Handlebars files must end with the extension .hbs (don’t add extension to file paths)
- Partials need to be defined in the options section when setting up fastify
- fastify-helmet provides extra security for NodeJs apps and will remove service information
- point-of-view is used for managing templates (also supports EJS and …)
- reply.view needs to pass in an empty object for data otherwise it won’t work.
Fastify
First we need to install Fastify and start the application, before we can look at adding in a route and a template
Note: All the code below goes into your server.js
Install
npm i --save fastify
Server.js
const fastify = require('fastify')({ logger: true })
// Create an endpoint for root / and rendering our template main, using the absolute file path
fastify.get('/', (req, reply) => {
reply.view('/views/layouts/main', {});
});
// Run the server!
const start = async () => {
try {
await fastify.listen(3000)
fastify.log.info(`server listening on ${fastify.server.address().port}`)
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
Fastify Helmet
Ideally we shouldn’t be exposing information of our service that we use to the public as this could make yourselfs vunerable to security treats or attacks. Therefore we want to hide the server information such as version, technology and procals. Helmet does all this out of the box for us, so lets add it to our code.
Install
npm i --save fastify-helmet
Server.js
const helmet = require('fastify-helmet')
fastify.register(
helmet
)
Rendering
Point of view is a way for us to render out our templates using one of a few supported engines. For our example we will use Handlebars
or hbs
for short. More information can be found here
Install
npm i --save point-of-view
Note: We will need to define all particals in the options section and files will be names with the extention .hbs
otherwise it won’t work.
For this example you will need to have at the root of your application (where your server.js file lives) a folder called views
. This will then contain your partials
dir and your layouts
dir that will contain all of your handlebars templates.
Example:
.
+-- server.js
+-- views
| +-- layouts
| +-- main.hbs
| +-- partials
| +-- head.hbs
| +-- header.hbs
| +-- footer.hbs
Server.js
fastify.register(require('point-of-view'), {
engine: {
handlebars: require('handlebars')
},
includeViewExtension: true,
options: {
partials: {
head: '/views/partials/head.hbs',
header: '/views/partials/header.hbs',
footer: '/views/partials/footer.hbs'
}
}
});
I would personally make the partials dynamically generated from the files in that directory but given this is a small project I didn’t feel the need to do that right now.
Fastify static
Install
npm i --save fastify-static
Server.js
// Set Static files path
fastify.register(require('fastify-static'), {
root: path.join(__dirname, '/public'),
prefix: '/public/', // optional: default '/'
})
Final code
Install all in one go and the final code inside my server.js
Install
npm i --save fastify fastify-helmet point-of-view fastify-static
Server.js
// Require the framework and instantiate it
const fastify = require('fastify')({ logger: true })
const helmet = require('fastify-helmet')
fastify.register(
helmet
)
fastify.register(require('point-of-view'), {
engine: {
handlebars: require('handlebars')
},
includeViewExtension: true,
options: {
partials: {
head: '/views/partials/head.hbs',
header: '/views/partials/header.hbs',
footer: '/views/partials/footer.hbs'
}
}
});
fastify.get('/', (req, reply) => {
reply.view('/views/layouts/main', {});
});
// Set Static files path
fastify.register(require('fastify-static'), {
root: path.join(__dirname, '/public'),
prefix: '/public/', // optional: default '/'
})
// Run the server!
const start = async () => {
try {
await fastify.listen(3000)
fastify.log.info(`server listening on ${fastify.server.address().port}`)
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()