Extending and configuring Express - Node.js in Action (2014)

Node.js in Action (2014)

Appendix C. Extending and configuring Express

Express does a lot right out of the box, but extending Express and tweaking its configuration can simplify development and allow it to do more.

C.1. Extending Express

Let’s start by looking at how to extend Express. In this section, you’ll learn how to

· Create your own template engines

· Take advantage of community-created template engines

· Improve your applications using modules that extend Express

C.1.1. Registering template engines

Engines may provide Express support out of the box by exporting an __express method. But not every engine will provide this, or you may want to write your own engines. Express facilitates this by providing the app.engine() method. In this section, we’ll take a look at writing a small markdown template engine with variable substitution for dynamic content.

The app.engine() method maps a file extension to a callback function so that Express knows how to use it. In the following listing, the .md extension is passed so that render calls such as res.render('myview.md') will use the callback function to render the file. This abstraction enables practically any template engine to be used within the framework. In this custom template engine, braces are used around local variables to allow for dynamic input—for example, {name} will output the value of name wherever it’s found in the template.

Listing C.1. Handling the .md extension

The template engine in listing C.1 will allow you to write dynamic markdown views. For example, if you want to greet a user with markdown, it might look like this:

# {name}

Greetings {name}! Nice to have you check out our application {appName}.

C.1.2. Templates with consolidate.js

The consolidate.js project was created exclusively for Express 3.x, and it provides a single unified API for many of Node’s template engines. This means Express 3.x allows you to use more than 14 different template engines out of the box, or if you’re working on a library that uses templates, you can take advantage of the wide selection of engines provided by consolidate.js.

For example, Swig is a Django-inspired template engine. It uses tags embedded within HTML to define logic, as shown here:

<ul>

{% for pet in pets %}

<li>{{ pet.name }}</li>

{% endfor %}

</ul>

Depending on the template engine and your editor’s syntax-highlighting support, you’ll probably be tempted to have HTML-style engines use the .html extension rather than an extension based on the engine name, such as .swig. You can do this with the Express app.engine() method. Once called, when Express renders a .html file, it will use Swig:

var cons = require('consolidate');

app.engine('html', cons.swig);

The EJS template engine would also likely be mapped to .html because it also uses embedded tags:

<ul>

<% pets.forEach(function(pet){ %>

<li><%= pet.name %></li>

<% }) %>

</ul>

Some template engines use an entirely different syntax, and it doesn’t make sense to map them to .html files. Jade is a good example of this, sporting its own declarative language. Jade could be mapped with the following call:

var cons = require('consolidate');

app.engine('jade', cons.jade);

For details and a list of supported template engines, visit the consolidate.js project repository at https://github.com/visionmedia/consolidate.js.

C.1.3. Express extensions and frameworks

You might be wondering what options are available for developers who use more structured frameworks, like Ruby on Rails. Express has several options for those situations.

The Express community has developed several higher-level frameworks built on top of Express to provide directory structure, as well as high-level, opinionated features, such as Rails-style controllers. In addition to these frameworks, Express also sports a variety of plugins to extend its functionality.

Express-Expose

The express-expose plugin can be used to expose server-side JavaScript objects to the client side. For example, if you wanted to expose the JSON representation of the authenticated user, you might invoke res.expose() to provide the express.user object to your client-side code:

res.expose(req.user, 'express.user');

Express-Resource

Another great plugin is express-resource, a resourceful routing plugin used for structured routing.

Routing can be accomplished in many ways, but it boils down to a request method and path, which is what Express provides out of the box. Higher-level concepts can be built on top.

The following example shows how you might define actions for showing, creating, and updating a user resource in a declarative fashion. First, here’s what you’d add in app.js:

app.resource('user', require('./controllers/user'));

And here’s what the controller module ./controllers/user.js would look like.

Listing C.2. The user.js resource file

exports.new = function(req, res){

res.send('new user');

};

exports.create = function(req, res){

res.send('create user');

};

exports.show = function(req, res){

res.send('show user ' + req.params.user);

};

For a full list of plugins, template engines, and frameworks, visit the Express wiki at https://github.com/visionmedia/express/wiki.

C.2. Advanced configuration

In the previous chapter, you learned how to configure Express using the app .configure() function, and we covered a number of configuration options. In this section, you’ll learn about additional configuration options that you can use to change default behavior and unlock additional functionality.

Table C.1 lists Express configuration options we didn’t discuss in chapter 8.

Table C.1. Built-in Express settings

default engine

Default template engine used

views

View lookup path

json replacer

Response JSON manipulation function

json spaces

Amount of spaces used to format JSON responses

jsonp callback

Support JSONP with res.json() and res.send()

trust proxy

Trust reverse proxy

view cache

Cache template engine functions

The views configuration option is fairly straightforward and is used to designate where view templates live. When you create an application skeleton on the command line using the express command, the views configuration option is automatically set to the application’s views subdirectory.

Now let’s look at a more involved configuration option: json_replacer.

C.2.1. Manipulating JSON responses

Suppose you have the user object set up with private properties such as an object’s _id. By default, a call to the res.send(user) method would respond with JSON such as {"_id":123,"name":"Tobi"}. The json replacer is a setting that takes a function that Express will pass toJSON.stringify() during res.send() and res.json() calls.

The standalone Express application in the following listing illustrates how you could use this function to omit properties starting with "_" for any JSON response. In this example, the response is now {"name":"Tobi"}.

Listing C.3. Using json_replacer to control and modify JSON data

var express = require('express');

var app = express();

app.set('json replacer', function(key, value){

if ('_' == key[0]) return;

return value;

});

var user = { _id: 123, name: 'Tobi' };

app.get('/user', function(req, res){

res.send(user);

});

app.listen(3000);

Note that individual objects, or object prototypes, can implement the .toJSON() method. This method is used by JSON.stringify() when converting an object to a JSON string. This is a great alternative to the json_replacer callback if your manipulations don’t apply to every object.

Now that you’ve learned how to control what data is exposed during JSON output, let’s look at how you can fine-tune JSON formatting.

C.2.2. JSON response formatting

The json spaces configuration setting affects JSON.stringify() calls in Express. This setting indicates the number of spaces to use when formatting JSON as a string.

By default, this method will return compressed JSON, such as {"name":"Tobi","age":2,"species":"ferret"}. Compressed JSON is ideal for a production environment, as it reduces the response size. But during development, uncompressed output is much easier to read.

The json spaces setting is automatically set to 0 in production; it’s set to 2 in development, which produces the following output:

{

"name": "Tobi",

"age": 2,

"species": "ferret"

}

C.2.3. Trusting reverse proxy header fields

By default, Express internals won’t trust reverse proxy header fields in any environment. Reverse proxies are out of scope for this book, but if your application is running behind a reverse proxy, such as Nginx, HAProxy, or Varnish, you’ll want to enable trust proxy so that Express knows these fields are safe to check.