Handlebars Templates in Cloudflare Workers

These days, the most prevalent pattern for web development is to have one or more APIs and a Single Page Application (SPA) that coordinates calling these APIs and representing their output as HTML in a dynamic, client-side web application. 

Especially with dedicated development frameworks and specialised build toolchains, this new approach is a great way to get to a result quickly and efficiently.

However, there is still a case to be made for server side rendered HTML. Indeed, newer frameworks like Vue and React offer a hybrid approach that lets you serve up parts of your HTML as interpreted on the server and made dynamic by rehydrating it on the client. 

That's not what this article is about. 

This article will demonstrate how to drag a somewhat old-school templating technology into a modern hosting stack: We're going to use Cloudflare's Workers to serve up Handlebars templates.

To do this we will take the following steps:

  • add a number of `handlebars` related dependencies to the Wrangler project;
  • add some scripts to your `package.json`; and
  • create a Custom build step in the wrangler.toml file


Constraints

Due to the set of technologies Cloudflare uses to make Workers possible, core Node modules are not available, including `fs`. Unfortunately, the full Handlebars engine uses it to load files from disk, disqualifying it from being used. 

The runtime-only version of Handlebars on the other hand is smaller and does not rely on these Node packages. 

The only prerequisite of using the Handlebars runtime-only version is that all templates must be precompiled. Our project needs to be extended with a Custom Build step in the `wrangler.toml` file.

Another constraint is that `require`-ing other files is not allowed without enabling the NodeJS compatibility layer, which will reduce performance of your Cloudflare Worker significantly. 

For bonus points, we have also extended the `registerHelper` functionality of Handlebars to support asynchronous operations so that we can perform more complicated operations if we so desire.

Project folder structure

The rest of these instructions assume the following:

  • You have a `views` folder in the root of your project
  • It has two sub-folders called `pages` and `partials`.
  • You have an `assets` folder in the root of your project

Adding dependencies

Add the following dependencies to your `package.json`:

									    "handlebars": "^4.7.7",
									    "hbs-async-render": "^1.0.1"
									

And add this to your `devDependencies` section:

									    "hbs-import-transpile": "^1.0.4"

The `handlebars` module provides the Handlebars runtime-only environment. The `hbs-async-render` module allows us to register and succesfully evaluate Handlebars helpers that return Promises rather than content. 

Lastly, we need the `hbs-import-transpile` for a compile-time file manipulation that will let us use the CommonJS output generated by the `handlebars` command line tool with `import` rather than `require`.

Adding `package.json` scripts

We are you going to add three new scripts to the `package.json` file. 
The first calls the handlebars command-line tool. If you have not yet installed, please make sure you run the command below:

									    $ npm install -g handlebars

The second will take the output from the tool and transpile it for use as an `import` rather than `require`. 

Lastly, we are adding another script target that can run these script in one go. 
 

									"handlebars": "npm run compilehbs && npm run transpilehbs && rm src/*-original.js",
									"compilehbs": "handlebars -e hbs -f src/pages-original.js views/pages/ && handlebars -e hbs -p -f src/partials-original.js views/partials/",
									"transpilehbs": "hbs-import-transpile src/pages-original.js > assets/pages.js && hbs-import-transpile src/partials-original.js > assets/partials.js"


The `rm` command in the handlebars target cleans up the output from `compilehbs`.


Updating `wrangler.toml` to run Custom Build step

Add the following to your `wrangler.toml` file to enable a custom build step that invokes the `handlebars` target from the `package.json`. 

									[build]
									command="npm run handlebars"
									watch_dir="views/"

Any time a file in the `views/` folder is touched, your Worker will recompile the handlebars templates, the rest of the codebase, and deploy it to your function in the cloud. 

Code Example

Now that we have configured out Cloudflare Workers project to precompile Handlebars templates, let's try creating some code with it. 

You can find the Github Repository here: 

Also Read

Deploying Flutter Web builds to Cloudflare Pages

By Marnix Kok on 20 September 2023