Dev Blog #2: Handlebars and Categories

Posted on 8/1/2020

At work, our applications are designed with a static front-end that uses $.ajax to make HTTP calls to an RPC-style API. I've only been around in the software world for a few years, but it's already pretty evident to me that RPC is associated with XML and is definitely not considered "hip" anymore, but I definitely understand why it works for our use cases, and why a strict REST API could get confusing. How do you model an HTTP request to send text messages out to a list of applications? With RPC, it's easy to write intuitive names for endpoints, like "/SendSmsToApplicants?applicantIds=%5B106%2C34%2C55%5D". I appreciate the structure and organization that a REST API gives you, but I also appreciate the flexibility that an RPC-style API gives you.

So of course when I started building the blog, my instinct was to user RPC-style URLs and patterns: "/blog.html?category=running" and "/post.html?postId=7". It works, but I realized that my use case is a little different from the applications I build for work for a few reasons:

I want this blog to be easily searchable and indexable. I know that Google has gotten much better over the years at indexing pages that are built dynamically, but I want the content to be available directly from the URL, and I want the URL structure to read like a directory.

Most of the applications I develop for work are very dynamic, so it kind of makes sense to defer some of the loading. They're filled with grids and forms and modals and "submit" buttons and dynamic maps, so rendering all of that on the server would probably be a waste of time. If I want to be able to re-draw the data on the map after something is updated, I'd have to build it on the server, then write a function to update it on the client, and maintain both of those. It's much simpler to just write the draw() function once on the client, and call that on page load and after updates. In contrast, the blog is essentially static. You pull in a few Kb of html, and it's ready to go.

An RPC-style API would require three network calls in series to finish building the page. The first retrieves basically an empty page, and then starts the second call to get the javascript files. Once all of the javascript has been downloaded and parsed, it makes a third call to get the actual data, and writes that on the screen. If I move all of that to the server, I can have the page ready to view after a single network call.

So I turned to my favorite dead-simple templating engine: handlebars.js. It describes itself as providing "minimal templating on steroids", which I think is pretty accurate. Right now, I'm using it as a glorified find-and-replace with a for-loop. I know it can be used to handle much more complicated scenarios, but the way it's working right now is so easy and I love it. Here's what the whole homepage looks like:

<html>  <head>    <link href="/css/coreypurcella.css" rel="stylesheet">  </head>  <body>    {{{header}}}    <div id="content">      <div id="posts">        {{#each posts}}        <div class="post">          <h2 class="title"><a href="/posts/{{postId}}">{{title}}</a></h2>          <div class="body">{{{body}}}...</div>          <a href="/posts/{{postId}}">Keep Reading...</a>          <br>        </div>        {{/each}}      </div>    </div>  </body></html>

So when someone requests the homepage, all I have to do is create an object with the header and the posts, and let handlebars handle the rest.

  var bars = require("handlebars");  var html = fs.readFileSync("static/index.html").toString();  var template = bars.compile(html);  var posts = await getPosts(params); // params optionally contains the category and page number  var header = fs.readFileSync("static/header.html").toString()  return template({posts: posts, header: header})

Handlebars then injects the header into the page, and loops through each object in "posts" and injects the postId, title, and body. Properties surrounded with double braces like {{postId}} are escaped so you can't use HTML formatting, but properties with triple braces like {{{body}}} are not escaped, so HTML formatting is preserved.

And that's it! template() returns an HTML string, and that's sent straight to the client. I love that handlebars doesn't ask you to make things complicated if you don't need to. It can be as simple as you need it to be.

Another side effect of this change is the new URL structure. Instead of using a gross query string, like /blog.html?cateogy=running&page=2 (which is totally what I would have done by default), you can view posts by category by adding it to the url after /blog, and change the page (nothing has more than one page of posts at this point) by adding that as the last part of the url:

app.get("/blog/:category/:page"...)

I think it makes intuitive sense to anyone who has ever used a file system. I've also added a header that links directly to each one of the categories.

I'm pleased with the new server-side rendering, and the menu and styling changes. I think this is starting to look more like a blog and less like My First HTML Page.