Chapter 4. Server-Side Templates

Most front-end developers have needed to work with server-side templates at some point. They may not have been called that—there was a time when these templates were simply called “PHP pages”, “JSPs”, or similar, before the push to apply separation of concerns on the web. These days it’s more common to see pages and views rendered by any back-end framework trimmed of as much business logic as possible.

Node is no different. Just as those other application frameworks need a way to separate the HTML produced from the data that populates it, so does ours. We want to be able to create a set of views loosely coupled to our application logic and have the application decide when to render them, and with what data.

Creating a Dynamic Page

Unlike other server frameworks, choosing Node does not implicitly choose the templating engine you’ll use for creating pages. There existed several templating engines for JavaScript when Node was created, and that number has only grown since. Thanks to Node, we now have a large number of server-side-only engines as well. Almost every JavaScript library of sufficient size offers a template engine, Underscore probably being the smallest, and there are many standalone options. Node frameworks also tend to have a default. Express uses Jade out of the box, for instance. It doesn’t really matter which you choose, as long as it meets your needs and is comfortable for you.

Some things you might look at when selecting a templating engine are:

Does it require any language besides JavaScript?

If so, you won’t be able to use it on the client.

Does it require the existence of a DOM?

If so, you’ll need to fake a DOM in Node to use it on the server—this can be done, but it adds another step, of course.

Does it allow templates to be compiled once and cached, before they’re first rendered?

This may be a concern if you want to do all your template parsing up front, or if you’ll render the same template many times.

Are there any restrictions on where or how templates are read into the template engine functions?

You may need to have templates come from a script element in the DOM, a module in Node, or from a string literal. Wherever your templates will be stored, you want a template engine that doesn’t expect them to be elsewhere.

How much logic is allowed in the template?

Some template engines aim to be logic-less, with a subset going as far as only allowing insertion of strings. Others will allow you to write blocks of JavaScript as long as you like within the template. Logic-less templates are often managed by an additional layer that prepares data for rendering. This aspect of the template engine can affect your architecture, so it’s worth doing some research.

To keep things simple, we’ll use Mustache templates for these examples. If you’ve never used a JavaScript template engine, Mustache is a good place to start because it’s right in the middle of the logic/no-logic spectrum, offering a subset of functions but not access to the whole of JavaScript. Additionally, the same syntax is used in lots of other template engines, which is a bonus in case your site eventually requires something more powerful or more minimal.

We already set up our application to accept values from a form collecting our user’s first and last name, but we never discussed the form itself. Moreover, we haven’t done a thing to help our user out should they want to change their submitted name. Our first server-side template will be an edit page for our user’s (somewhat limited) information:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Edit your user information</title>
  </head>
  <body>
    <h1>Edit your user information</h1>
    <form action="/" method="POST">
      <label>First name: 
        <input type="text" name="firstName" value="{{firstName}}" />
      </label>
      <label>Last name: 
        <input type="text" name="lastName" value="{{lastName}}" />
      </label>
      <input type="submit" value="Save" />
    </form>
  </body>
</html>

The double curly braces around firstName and lastName are the delimiters that tell Mustache where to substitute in the data we’ll pass it when it’s time to render the page. We’re using the same property names for the existing values as we use for the name of the input elements to make things easy to keep track of, but the name, ID, and CSS class in the HTML are irrelevant to Mustache (which is one way it differs from some other template engines). To use our template, we need to install Mustache with npm, then modify our existing code to render the template instead of building the response HTML out of concatenated strings:

var connect = require("connect"),
  fs = require("fs"),
  mustache = require("mustache");
      
connect(
  connect.bodyParser(),
  function(req, res) {
    var userName = {
        firstName: req.body.firstName,
        lastName: req.body.lastName
      },
      // create and open the stream
      tmplFile = fs.createReadStream(
        __dirname + "/public/edit.html", 
        {encoding: "utf8"}
      ),
      template = "",
      html;
      
    tmplFile.on("data", function(data) {
      template += data;
    });
    tmplFile.on("end", function() {
      
      // render the template with the userName object as data
      html = mustache.to_html(template, userName);
        
      res.end(html);
   
    });
  }
).listen(8000);

Assuming we stored our template as edit.html in the public directory, this code will stream the contents into a variable in our application and, once the template is fully loaded, pass it the submitted first and last name to render as HTML. Then we send the HTML back like we normally would.

We’ve switched from fs.readFile() to reading the template content from a stream above, which would normally make things more efficient, except that we don’t need to load these templates over and over again or wait for them to be requested to know we’ll need them. Going forward, we’ll treat our templates as dependencies, and load them accordingly. It’s good to note that there’s more than one way to read a file, though.

Get Node for Front-End Developers now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.