O'Reilly logo

Node for Front-End Developers by Garann Means

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

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.

Partial Templates

There will be certain pieces of the pages in our site that are repeated. Things like headers, footers, and certain application-specific widgets will appear on multiple pages, or every page. We could copy the markup for those pieces into the page’s template, but it will be easier to manage a single shared template for each of these common elements. Copying and pasting a reference to a child template is more maintainable than copying and pasting the markup itself. If we don’t want to have to repeat the code that imports our CSS and client-side JavaScript, we can templatize a default version of the pages in our site, storing all that information there, in addition to site-wide headers and footers. We can leave a space for the content of each page and reuse the rest. Let’s create a file with this boilerplate markup called parent.html:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{pageTitle}}</title>
    <link rel="stylesheet" src="css/style.css" />
    {{>stylesheets}}
  </head>
  <body>
    {{>content}}
    <footer>&copy; 2011 Node for Front-End Developers</footer>
  </body>
  {{>scripts}}
</html>

Every template engine handles nested templates differently. While Mustache uses the {{>...}} syntax you see above, templating engines that are otherwise very similar to Mustache may use something different. Try not to get too caught up in the particular syntax of this engine, just know that the greater-than sign here indicates a child template, and we’ll use that to do composition.

To allow this parent template to load individual child templates and create full pages, we’ll have to add some robustness into our application code. Instead of loading and rendering templates inline, we’ll load another third-party module to manage loading, and move rendering to its own function.

The requirejs module works like the client-side Require.js utility, providing a slightly different approach to dependency management than Node’s. The difference we’re taking advantage of below is the text.js extension. We’ll need to download this from the Require.js site and save it in our Node application’s root directory. This will allow Require to load and manage text files, guaranteeing that they’re loaded efficiently and available when they’re needed. Adding the prefix text! to the path of the dependency (our template, in this case) tells Require to load the file as text instead of trying to evaluate it:

var connect = require("connect"),
  fs = require("fs"),
  mustache = require("mustache"),
  requirejs = require("requirejs"),
  parentTmpl;
      
// configure requirejs to fall back to Node's require if a module is not found      
requirejs.config({ nodeRequire: require });
      
connect(
  connect.static(__dirname + "/public"),
  connect.router(function(app) {
    app.get("/show/:tmpl/:firstName/:lastName", function(req, res) {
      var userName = {
          firstName: req.params.firstName,
          lastName: req.params.lastName
        };
      // once the parent template is loaded, render the page
      requirejs(["text!public/parent.html"], function(_parentTmpl) {
        parentTmpl = _parentTmpl;
        render(res, req.params.tmpl + ".html", userName);
      });
    });
  })
).listen(8000);
    
function render(res, filename, data, style, script, callback) {
  // load the template and return control to another function or send the response
  requirejs(["text!public/" + filename], function(tmpl) {
    if (callback) {
      callback(res, tmpl, data, style, script);
    } else {
      // render parent template with page template as a child
      var html = mustache.to_html(
        parentTmpl, 
        {content: data}, 
        {content: tmpl, stylesheets: style || "", scripts: script || ""}
      );
      res.end(html);
    }
  });
}

While we could make our system for rendering templates within a wrapper far more robust, this smaller amount of code will let us do quite a bit (within certain constraints). In addition to the content for each page, our parent template and its rendering function will accept additional CSS or JavaScript imports as string literals. We can allow the render function to send back the rendered HTML without modification, or we can override that behavior by passing in an additional callback to call once the child template loads.

Now that we have files being loaded and requests being made in order, you can begin to see the issues with nesting callbacks that frighten some people who are new to Node. We’ve abstracted out some functionality, but there are still anonymous functions and nested logic. If you’re used to DOM scripting, this probably isn’t as scary, but you’ve probably also recognized it as something that needs to be managed. Although the code above is all in a single file for clarity, in a real application we’d put all of our template rendering code into its own module and move the logic into named functions private to that module.

Parsing Other File Types

If we wanted to dynamically generate CSS (maybe for use in a theme builder of some sort) or JavaScript (perhaps cherry-picking pieces of a more comprehensive JS library), all we have to do is change the file type we’re reading in. Because we read in the files as strings, it makes no difference if they’re something Node could technically try to execute. Here again, Node’s lack of out-of-the-box handling for common file types works to our advantage. Any file can be an application, a library, a template, or a static resource—it’s entirely up to us and how we treat it.

Let’s say that we do indeed have a site offering some kind of customizable client-side widget library. For example, assume we have a customizable CSS file using Mustache to populate user-defined values:

h1 { color: #{{main}}; }
h2 { color: #{{secondary}}; } 
input { border-style: {{border}}; border-radius: {{corners}}px; }

To provide our end users with CSS and JavaScript matching their site’s needs, we only really need to change our routing. We’ll assume the values we’re receiving are coming from a form post, and that the rest of our application file remains the same:

connect(
  connect.static(__dirname + "/public"),
  connect.bodyParser(),
  connect.router(function(app) {
    app.post("/theme", function(req, res) {
      var theme = {
        main: req.body.mainColor,
        secondary: req.body.secondaryColor,
        border: req.body.borderStyle,
        corners: req.body.borderRadius
      };
      // load and render the CSS template
      requirejs(["text!public/css/theme.css"], function(tmpl) {
        var css = mustache.to_html(tmpl, theme);
        res.writeHead(200, {
          "Content-Type": "text/css",
          "Content-Length": css.length
        });
        res.end(css);
      });
    };
    app.post("/builder", function(req, res) {
      var options = {
        shim: req.body.html5shim,
        flash: req.body.useFlash,
        sockets: req.body.useWebSockets,
        jsonp: req.body.useJsonp
      };
      // load and render the JS template
      requirejs(["text!public/js/builder.js"], function(tmpl) {
        var js = mustache.to_html(tmpl, options);
        res.writeHead(200, {
          "Content-Type": "application/javascript",
          "Content-Length": js.length
        });
        res.end(js);
      });
    };
  })
).listen(8000);

Since we don’t want our CSS or JavaScript composed into our parent template, we can skip the render function and recreate its functionality for both MIME types we’ll want to return. You can probably see how the logic to render and return a response could be abstracted out into a function of its own, but since our example application only handles these two scenarios, the less efficient code above will get the job done.

Creating Files on the Fly

An in-depth exploration of any of the necessary modules is out of scope for this guide, but there are numerous libraries to help you generate other types of files from within Node. While these aren’t templates in the same sense as the templates we’ve looked at so far, they can map directly to a filename in a URL and be built and served in a way that’s invisible to the user, just like templates. For instance, you might have cause to dynamically generate image files to do resizing or add watermarks. Modules to do that will have their own APIs, and can be loaded and used like any template engine or other module. To keep your application code clean, you’d probably choose to keep the code you write to work with those APIs in a separate file, the same way an external template stores information specific to rendering HTML or other text-based file types.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required