Serving Angle Brackets


In this series of blog posts, we’re looking at how to set up a simple web app in node.

In the previous installment we got the basic set up running. In this post we’re going to put the plumbing in for serving html, both static and dynamic pages.

Serving static files

We could serve static files using express to match on a given path like this app.get('/filename', ...) and returning the file in the response. However, this gets quite tedious. A much easier approach is to set up some middleware to handle static files.

Hold on, what’s middleware?

When express processes an incoming HTTP request, you can register your own code to run as part of this pipeline. Middleware is the opportunity to hook into all incoming requests, or just requests to a specific path. For example, you could register some middleware to ensure that requests are authenticated, or to decode specific content types.

We’ll register some middleware which will intercept requests matching the name of a static file and serve it for us.

To do this, add this line to server.js:

var path = require('path');
app.use(express.static(path.join(__dirname, 'public')));

Let’s unpack this line of code:

  1. require('path') will load a module for working with directory names. It’s a module that comes with node, so you don’t need to use npm to install it.
  2. __dirname is a variable in node, giving us the name of the directory which contains the script file.
  3. path.join will append file system paths together. So here we’re appending ‘public’ to the current directory.
  4. express.static is the middleware which serves static files.
  5. app.use registers middleware for every request.

So this line instructs the express app to serve static files from the ‘public’ directory.

Express is sensitive to the order middleware is registered, you need to add it above any of your routes, but after the app is created.

Your server.js should now look like this:

// load the path package

var path = require('path');
// load the express package
var express = require('express');

// create an express application
var app = express();

// register middleware to serve static pages
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', function(req, res){
res.send('hello world');
});

// start listening on port 8080
app.listen(8080);

Now, lets create a public folder:

> mkdir public

You can add a file in there, perhaps a favicon (favicon.ico).

You’ll have to restart your node process, but you should see your file is now being served.

This allows you to serve static assets from your node app, without your application code needing to be concerned about what the files are. Any request that comes in and matches a filename will be automatically served for you.

Server-side templating

We don’t want to just serve static file with node, we want to use some logic to create dynamic content.

My preference is to use mustache templates for converting data into html. So let’s plug mustache in.

Hogan.js is a JavaScript implementation of Mustache, and hogan-express allows you to easily hook it up to express.

Install the module using npm:

> npm install hogan-express --save

Now let’s register the middleware to handle the views:

app.engine('html', require('hogan-express'));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
app.set('layout', 'layout');

These lines can be placed immediately above the app.use we wrote earlier for the static files.

Let’s unpack this code line by line:

  1. app.engine registers a templating engine. Here we pass in the ‘hogan-express’ module, and register it as the templating engine for html.
  2. app.set sets an application setting. First we set the views setting, which is the directory where the views are stored. We use the path module again to refer to a sub-directory called views.
  3. We set the default view engine to be html (which refers to the one we registered on the first line).
  4. Finally the layout variable is the name of the view which is used as the template.

Now let’s create a directory for our views;

> mkdir views

We can add a layout.html file to this directory, which will contain our template:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title> { { title } } </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"></style>
</head>
<body>
{ { { yield } } }
</body>
</html>

I have had to put spaces between my curly braces, as jekyll got upset when it saw multiple curly braces next to each other. Please remove these spaces in your views

Note that we’re referring to the bootstrap stylesheet hosted on a CDN.

Now let’s create a simple view to show the current time.

<div class="container">
The time is : { { time } }
</div>

Call this time.html, and add it to the views directory.

Now we need to go back to our server.js file and wire it up.

Replace the app.get('/' ...) code with this:

app.get('/', function(req, res){
res.locals.time = new Date();
res.locals.title = 'the current time';
res.render('time');
});

Let’s unpack the code:

  1. We create a new data object (which is initialised with the current time), and sets a time property on a res.locals variable. res.locals allows you to pass data into the view.
  2. We also use res.locals to set the title of the page, which is in the layout.html template.
  3. Finally we tell express to render the response with the time template.

Our server.js file now looks like this:

// load the path package

var path = require('path');
// load the express package
var express = require('express');

// create an express application
var app = express();

// set up hogan for rendering views
app.engine('html', require('hogan-express'));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
app.set('layout', 'layout');

// register middleware to serve static pages
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', function(req, res){
res.locals.time = new Date();
res.locals.title = 'the current time';
res.render('time');
});

// start listening on port 8080
app.listen(8080);

That’s it!

We could of course listen to more paths, and create more views.

Next we’ll look at reading and writing to a database - the node way!