All Articles

Fastify using handlebars engine

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()