citizen

A server-side MVC and caching framework for Node.js web applications.

citizen

citizen is an event-driven MVC and caching framework for Node.js web applications. It's for people who are more interested in quickly building fast, scalable apps than digging around Node's guts or building a Jenga tower made out of 20 different packages. Use it as a foundation for a traditional server-side web application, a modular single-page app, or a RESTful API to be consumed by your front end framework.

  • Very high performance (even without caching)
  • Zero-configuration server-side routing with SEO-friendly URLs
  • Serve HTML, JSON, and JSONP from the same controller/view with a single URL flag
  • Optional in-memory caching of entire routes, individual controllers, files, and objects
  • Directives that make it easy to set cookies, sessions, redirects, caches, and more
  • Easily chain controllers or include controllers within other controllers
  • Do it all on the server or roll in your favorite client-side templating engine
  • Support for Jade and Handlebars templates with more on the way

citizen's API is stabilizing, but it's still subject to change. Always consult the change log before upgrading.

Have questions, suggestions, or need help? Send me an e-mail. Want to contribute? Pull requests are welcome.

These commands will create a new directory for your web app, install citizen, use its scaffolding CLI to create the app's skeleton, and start citizen with the web server listening on port 8080 (you can change the port to whatever you want):

$ mkdir mywebapp
$ cd mywebapp
$ npm install citizen
$ node node_modules/citizen/util/scaffold skeleton -n 8080
$ node app/start.js

If everything went well, you'll see confirmation in the console that citizen is listening on the specified port. Go to http://127.0.0.1:8080 in your browser and you'll see a bare index template.

For configuration options, see Configuration. For more utilities, see Utilities.

Please see Github for the complete readme, because npmjs.com truncates it, which breaks all these nice links I've created for you.

app/
  config/
    citizen.json        // Optional default config
    local.json          // Optional configs for different environments
    qa.json
    production.json
  logs/                 // Log files created by citizen and your app
    app.txt
    citizen.txt
  on/                   // Optional application events
    application.js
    request.js
    response.js
    session.js
  patterns/
    controllers/
      index.js
    models/
      index.js          // Optional for each controller
    views/              // You can use Jade (.jade), Handlebars (.hbs), or HTML files
      error/            // Optional views for error handling
        404.jade
        500.jade
        error.jade      // Default error template
      index/
        index.jade      // Default index view
        index-alt.jade  // Optional alternate index view
  start.js
web/                    // public static assets

citizen prefers convention over configuration, but some things are best handled by a config file.

The config directory is optional and contains configuration files that drive both citizen and your app in JSON format. You can have multiple citizen configuration files within this directory, allowing different configurations based on environment. citizen retrieves its configuration file from this directory based on the following logic:

  1. citizen parses each JSON file looking for a hostname key that matches the machine's hostname. If it finds one, it loads that configuration.
  2. If it can't find a matching hostname key, it looks for a file named citizen.json and loads that configuration.
  3. If it can't find citizen.json or you don't have a config directory, it runs under its default configuration.

The following represents citizen's default configuration, which is extended by your configuration file:

{
  "hostname":             "",
  "citizen": {
    "mode":               "production",
    "http": {
      "hostname":         "127.0.0.1",
      "port":             80
    },
    "https": {
      "hostname":         "127.0.0.1",
      "port":             443,
      "secureCookies":    true
    },
    "connectionQueue":    undefined,
    "format": {
      "html": {
        "pretty":         true
      },
      "json": {
        "urlDelimiter":   "-",
        "pretty":         2
      },
      "jsonp": {
        "urlDelimiter":   "-",
        "pretty":         2
      }
    },
    "gzip": {
      "enable":           false,
      "force":            false,
      "mimeTypes":        "text/plain text/html text/css application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml"
    },
    "cache": {
      "static":           false,
      "control":          {},
      "invalidUrlParams": "warn"
    },
    "sessions":           false,
    "sessionTimeout":     1200000,
    "requestTimeout":     30000,
    "prettyHTML":         true,
    "prettyJSON":         2,
    "log": {
      "toConsole":        false,
      "toFile":           false,
      "path":             "/absolute/path/to/app/logs",
      "defaultFile":      "citizen.txt"
    },
    "debug": {
      "output":           "console",
      "depth":            2,
      "disableCache":     true,
      "jade":             false
    },
    "urlPaths": {
      "app":              "/"
    },
    directories: {
      "app":              "(resolved based on location of start.js)",
      "logs":             "(directories.app)/logs",
      "on":               "(directories.app)/on",
      "controllers":      "(directories.app)/patterns/controllers",
      "models":           "(directories.app)/patterns/models",
      "views":            "(directories.app)/patterns/views",
      "web":              "(up one directory from directories.app)/web"
    }
  }
}

These settings are exposed publicly via app.config.hostname and app.config.citizen.

Note: This documentation assumes your global app variable name is "app", but you can call it whatever you want. Adjust accordingly.

Let's say you want to run an app on port 8080 in your local dev environment and you have a local database your app will connect to. You could create a config file called local.json (or myconfig.json, whatever you want) with the following:

{
  "hostname":             "My-MacBook-Pro.local",
  "citizen": {
    "mode":               "development",
    "http": {
      "port":             8080
    }
  },
  "db": {
    "server":             "localhost",
    "username":           "dbuser",
    "password":           "dbpassword"
  }
}

This config would extend the default configuration only when running on your local machine; you'll never accidentally push a test config to production again ;)

The database settings would be accessible within your app via app.config.db. The citizen and hostname nodes are reserved for the framework. Create your own node(s) to store your custom settings.

Here's a complete rundown of citizen's settings and what they mean:

citizen config options
Setting Values Description
hostname

The operating system's hostname

To load different config files in different environments, citizen relies upon the server's hostname as a key. At startup, if citizen finds a config file with a hostname key that matches the server's hostname, it chooses that config file. This is different from the HTTP hostname setting under the citizen node (see below).
citizen
mode
  • production
  • development
  • debug

Default: production

The application mode determines certain runtime behaviors. Production mode silences most logging and enables all application features. Development mode also silences most logs, but allows you to edit view templates on the fly without restarting the app. In addition to on-the-fly view editing, debug mode enables verbose logging and disables caching.
connectionQueue

A positive integer

Default: null

The maximum number of incoming requests to queue. If left unspecified, the operating system determines the queue limit.
sessions

Boolean

Default: false

Enables the user session scope, which assigns each visitor a unique ID and allows you to store data associated with that ID on the server.
sessionTimeout

Positive integer

Default: 1200000

If sessions are enabled, this number represents the length of a user's session in milliseconds. Sessions automatically expire once this time limit is reached.
requestTimeout

Positive integer

Default: 30000

Determines how long the server will wait for a response from your controllers before timing out, in milliseconds.
citizen.format
citizen.format.html
pretty

Boolean

Default: true

By default, rendered HTML sourced from Jade templates includes the original whitespace and line breaks. Change this setting to false to remove whitespace and minimize file size.
citizen.format.json
pretty

Boolean

Default: 2

JSON output is pretty by default. This setting controls how many spaces to use for indentations. Set it to false to remove whitespace from JSON output.
urlDelimiter

String

Default: -

When using the output URL parameter, this setting determines how to parse a JSON request. See "JSON and JSONP" for details.
citizen.format.jsonp
pretty

Boolean

Default: 2

JSONP output is pretty by default. This setting controls how many spaces to use for indentations. Set it to false to remove whitespace from JSON output.
urlDelimiter

String

Default: -

When using the output URL parameter, this setting determines how to parse a JSONP request. See "JSON and JSONP" for details.
citizen.gzip
enable

Boolean

Default: false

Enables gzip compression for rendered views and static assets. Compression occurs on the fly, but compressed routes can be cached with the cache directive, and static assets can be cached using the cache setting below.
force

Boolean

Default: false

Forces gzip encoding for all clients, even if they don't report accepting gzip. Many proxies and firewalls break the Accept-Encoding header that determines gzip support, and since pretty much all modern clients support gzip, it's probably safe to force it by setting this to true.
mimeTypes

String

A space-delimited list of MIME types that should be gzipped if gzip is enabled. See the sample config above for the default list. If you want to add or remove items, you must replace the list in its entirety.
citizen.cache
static

Boolean

Default: false

When serving static files, citizen normally reads the file from disk for each request. You can speed up static file serving considerably by setting this to true, which enables the static file cache.
control

Key/value pairs

Default: {}

Use this setting to set Cache-Control headers for static assets. The key is the pathname of the asset, and the value is the Cache-Control header. See Client-Side Caching for details.
invalidUrlParams

String

Default: warn

Determines the outcome when citizen attempts to cache a route or controller based on a URL with invalid cache parameters. The default is to log a warning and continue serving the request without caching. Set this to throw to throw an error instead. See the Cache section for instructions on caching.
citizen.log
toConsole

Boolean

Default: false

citizen only writes to the console when in debug mode. To override this behavior, set this to true.
toFile

Boolean

Default: false

citizen only writes to a log file when in debug mode. To override this behavior, set this to true.
path

String

Default: /path/to/app/logs

citizen writes log files to the logs directory in your app folder by default. Enter a different absolute path in this setting to change the location.
defaultFile

String

Default: citizen.txt

When file logging is enabled, citizen will write a file to the logs directory using this name. Change this setting to write your app logs to a different file. citizen will continue to write framework logs to citizen.txt, however.
citizen.debug
output
  • view
  • console

Default: console

In debug mode, citizen dumps debug info to the console. Change this setting to view to display the debug output in your browser.
depth

Positive integer

Default: 2

When citizen dumps an object in the debug content, it inspects it using Node's util.inspect. This setting determines the depth of the inspection, meaning the number of nodes that will be inspected and displayed.
disableCache

Boolean

Default: true

Debug mode disables the cache. Change this setting to false to enable the cache in debug mode.
jade

Boolean

Default: false

Jade's template debugging is quite verbose, so it's disabled by default, but you can enable it with this setting if citizen is failing to start due to template parsing errors and you need additional info.
citizen.urlPaths
app

String

Default: /

Denotes the URL path leading to your app. If you want your app to be located at http://yoursite.com/my/app, this setting should be /my/app (don't forget the leading slash). This setting is required for the router to work.
citizen.http
hostname

A valid hostname

Default: 127.0.0.1

The hostname at which your app can be accessed via HTTP. You need to configure your server's DNS settings to support this setting. Don't confuse this with the host machine's hostname setting above, which is different.
port

A valid port number

Default: 80

The port number on which citizen's HTTP server should listen for requests.
citizen.https
hostname

A valid hostname

Default: 127.0.0.1

The hostname at which your app can be accessed via HTTPS. You need to configure your server's DNS settings to support this setting. Don't confuse this with the host machine's hostname setting above, which is different.
port

A valid port number

Default: 443

The port number on which citizen's HTTPS server should listen for requests.
secureCookies

Boolean

Default: true

By default, all cookies set during an HTTPS request are secure. Set this option to false to override that behavior, making all cookies insecure and requiring you to manually set the secure option in the cookie directive.

The start.js file in your app directory can be as simple as this:

// start.js

global.app = require('citizen');

app.start();

Run start.js from the command line:

$ node start.js

Other ways to use app.start():

// Start an HTTP server with inline hostname and port, overriding the config
app.start({
  hostname: 'www.mysite.com',
  port: 8282
});

// Start an HTTPS server with key and cert PEM files
app.start({
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
});

// Start an HTTPS server with a PFX file running on port 3000
app.start({
  port: 3000,
  pfx: fs.readFileSync('mysite.pfx')
});

If you pass app.start() either a key/cert pair or PFX file, it assumes you want HTTPS.

When starting an HTTPS server, in addition to the hostname and port options, app.start() takes the same options as Node's https.createServer() (which takes the same options as tls.createServer()).

Objects created by citizen
app.start() Starts either an HTTP or HTTPS server.
app.cache()
app.exists()
app.retrieve()
app.clear()
app.listen()
app.copy()
app.extend()
app.isNumeric()
app.size() app.log()
app.dashes()
Helpers used internally by citizen, exposed publicly since you might find them useful
app.models Contains models from your supplied patterns, which you can use instead of require. Controllers and views aren't exposed this way because you don't need to access them directly.
app.handlebars A pointer to the citizen Handlebars global, allowing you full access to Handlebars methods such as app.handlebars.registerHelper()
app.jade A pointer to the citizen Jade global
app.config The configuration settings you supplied at startup
CTZN The global namespace used by citizen for internal objects, user sessions, and cache. You should not access or modify this namespace directly; anything you might need in your application will be exposed by the server to your controllers through local scopes.

Apps using citizen have a simple URL structure that determines which controller and action to fire, passes URL parameters, and makes a bit of room for SEO-friendly content that can double as a unique identifier. The structure looks like this:

http://www.site.com/controller/content-description/action/myAction/param/val/param2/val2

For example, let's say your site's base URL is:

http://www.cleverna.me

Requesting that URL would cause the index controller's default action to fire, because the index controller is the default. The following URL would also cause the index controller to fire:

http://www.cleverna.me/index

If you have an article controller, you'd request it like this:

http://www.cleverna.me/article

Instead of query strings, citizen uses an SEO-friendly method of passing URL parameters consisting of name/value pairs. If you had to pass an article ID of 237 and a page number of 2, you'd append name/value pairs to the URL:

http://www.cleverna.me/article/id/237/page/2

Valid parameter names may contain letters, numbers, underscores, and dashes, but must start with a letter or underscore.

The default controller action is handler(), but you can specify alternate actions with the action parameter (more on this later):

http://www.cleverna.me/article/action/edit

citizen also lets you optionally insert relevant content into your URLs, like so:

http://www.cleverna.me/article/My-Clever-Article-Title/page/2

This SEO content must always follow the pattern name and precede any name/value pairs. You can access it generically via route.descriptor or specifically via the url scope (url.article in this case), which means you can use it as a unique identifier (more on URL parameters in the Controllers section).

citizen relies on a simple model-view-controller convention. The article pattern mentioned above might use the following structure:

app/
  patterns/
    controllers/
      article.js
    models/
      article.js
    views/
      article/        // Matches the controller name
        article.jade  // Matches the controller name, making it the default view
        edit.jade     // Secondary view for editing an article

At least one controller is required for a given URL, and a controller's default view directory and default view file must share its name. Additional views should reside in this same directory. More on views in the Views section.

Models and views are optional and don't necessarily need to be associated with a particular controller. If your controller doesn't need a model, you don't need to create one. If your controller is going to pass its output to another controller for further processing and final rendering, you don't need to include a matching view. (See the controller handoff directive.)

A citizen controller is just a Node module. Each controller requires at least one public method to serve as an action for the requested route. The default action should be named handler(), which is called by citizen when no action is specified in the URL.

// article controller

module.exports = {
  handler: handler
};

// Required if no action is specified in the route
function handler(params, context, emitter) {

  // do some stuff

  emitter.emit('ready', {
    // content and directives for the server
  });
}

The citizen server calls handler() after it processes the initial request and passes it 3 arguments: an object containing the parameters of the request, the current request's context generated by the app up to this point, and an emitter for the controller to emit when it's ready to pass the results to the server.

Contents of the params argument
request The request object generated by the server, just in case you need direct access
response The response object generated by the server
route Details of the route, such as the requested URL and the name of the route (controller)
url Any URL parameters that were passed including the descriptor, if provided
form Data collected from a POST
payload Data collected from a PUT
cookie An object containing any cookies that were sent with the request
session An object containing any session variables, if sessions are enabled

In addition to having access to these objects within your controller, they are also included in your view context automatically so you can reference them within your view templates as local variables (more details in the Views section).

For example, based on the previous article URL...

http://www.cleverna.me/article/My-Clever-Article-Title/page/2

...you'll have the following params.url object passed to your controller:

{
  article: 'My-Clever-Article-Title',
  page: '2'
}

Note that "numeric" URL parameters are stored as strings in the url scope; this is because there are too many factors for citizen to make a reasonable assumption regarding your app's expectations. For example:

http://www.cleverna.me/user/activationCode/023498721250

Your app would likely expect the leading zero to remain intact in this case, but this value is technically numeric in JavaScript, and if stored as a number would see the zero dropped. Large integers beyond what JavaScript can accurately represent also present issues. Use the provided convenience functions (isNumeric, isInteger, and isFloat) to assist in dealing with numbers.

The controller name becomes a property in the URL scope that contains the descriptor, which makes it well-suited for use as a unique identifier. This content is also available in the route object as route.descriptor.

The context argument contains any output that's been generated by the request up to this point. There are various events that can populate this argument with content and directives, which are then passed to your controller so you can access that content or see what directives have been set by previous events.

The emitter argument is the method by which the controller lets the server know that it's done with its tasks and ready to render the result. The emitter should emit a ready event when the controller has accomplished its task. This lets the server know it's okay to proceed. As part of this event, the emitter should include any view content and directives for the server.

Using the above URL parameters, I can retrieve the article content from the model and pass it back to the server:

// article controller

module.exports = {
  handler: handler
};

function handler(params, context, emitter) {
  // Get the article content
  var article = app.models.article.getArticle(params.url.article, params.url.page);

  // Emit the 'ready' event and pass any objects you want added to the view
  // context via the content object
  emitter.emit('ready', {
    content: article
  });
};

Alternate actions can be requested using the action URL parameter. For example, maybe we want a different action and view to edit an article:

// http://www.cleverna.me/article/My-Clever-Article-Title/page/2/action/edit

// article controller

module.exports = {
  handler: handler,
  edit: edit
};

function handler(params, context, emitter) {
  // Get the article content
  var article = app.models.article.getArticle(params.url.article, params.url.page);

  // Emit the 'ready' event and pass any objects you want added to the view
  // context via the content object
  emitter.emit('ready', {
    content: article
  });
};

function edit(params, context, emitter) {
  // Get the article content
  var article = app.models.article.getArticle(params.url.article, params.url.page);

  // Use the /patterns/views/article/edit.jade view for this action (more on
  // alternate views in later sections).
  emitter.emit('ready', {
    content: article,
    view: 'edit'
  });
};

The second argument in emitter.emit is an object containing any data you want to pass back to citizen. All the content you want to render in your view should be passed to citizen within an object called content, as shown above. Additional objects can be passed to citizen to set directives that provide instructions to the server (explained later in the Emitter Directives section). You can even add your own objects to the request context and pass them from controller to controller (more in the Controller Handoff section.)

The emitter has an error event that gives you more control over how errors are handled. The error event throws a JavaScript error to enable debugging; citizen prevents the error from bubbling up to the Node process, however, so your app won't crash.

// article controller

module.exports = {
  handler: handler
};

function handler(params, context, emitter) {
  // Get the article content
  var article = app.models.article.getArticle(params.url.article, params.url.page);

  // If everything is fine, emit ready
  if ( article ) {
    emitter.emit('ready', {
      content: article
    });

  // If there's a problem, handle the error
  } else {
    emitter.emit('error', {

      // Optional. Default status code is 500 (server error).
      statusCode: 404,

      // Optional message you want to display, which overrides citizen's messaging
      message: 'The requested article does not exist.'
    });
  }

};

If you use the listen() method for asynchronous functional calls, the status output in output.listen is formatted to coincide with citizen's error structure, allowing you to dump it straight into an error emitter:

// article controller

module.exports = {
  handler: handler
};

function handler(params, context, emitter) {
  // Get the article content using listen()
  app.listen({
    article: function (emitter) {
      app.models.article.getArticle(params.url.article, params.url.page, emitter);
    }
  }, function (output) {

    if ( output.listen.success && output.article ) {

      emitter.emit('ready', {
        content: output.article
      });

    } else {

      emitter.emit('error', output.listen);

    }
  });
};

Errors are returned in the format requested by the route. If you request JSON with the /format/json URL parameter and the route throws an error, the error will be in JSON format.

The app skeleton created by the scaffold utility includes optional error view templates for common client and server errors, but you can create templates for any HTTP error code (more in the Views section under Error Views).

To make a controller private—inaccessible via HTTP, but accessible within your app—add a plus sign (+) to the beginning of the file name:

app/
  patterns/
    controllers/
      +_header.js  // Partial, only accessible internally
      _head.js     // Partial, accessible via www.cleverna.me/_head
      article.js   // Accessible via www.cleverna.me/article

If you want to make a controller available to third party sites, see the CORS section.

Models are optional and their structure is completely up to you. citizen doesn't talk to your models directly; it only stores them in app.models for your convenience.

Here's a simple static model for the article pattern (just an example, because storing content this way is awful):

// article model

exports.getArticle = getArticle;

function getArticle(article, page) {
  var articles = {
    'My-Clever-Article-Title': {
      title: 'My Clever Article Title',
      summary: 'Am I not terribly clever?',
      pages: {
        '1': 'First page content',
        '2': 'Second page content'
      }
    },
    'Clever-Part-II-The-Sequel': {
      title: 'Clever Part II: The Sequel',
      summary: 'Too clever for just one article.',
      pages: {
        '1': 'First page content',
        '2': 'Second page content'
      }
    }
  };

  return {
    title: articles[article]['title'],
    summary: articles[article]['summary'],
    text: articles[article]['pages'][page]
  };
};

citizen supports Jade and Handlebars templates, as well as good old HTML. You can even mix and match Jade, Handlebars, and HTML templates as you see fit; just use the appropriate file extensions (.jade, .hbs, or .html) and citizen will compile and render each view with the appropriate engine.

You have direct access to each engine's methods via app.handlebars and app.jade, allowing you to use methods like app.handlebars.registerHelper() to create global helpers. Keep in mind that you're extending the global Handlebars and Jade objects, potentially affecting citizen's view rendering if you do anything wacky because citizen relies on these same objects.

In article.jade, you can reference objects you placed within the content object passed by the emitter. citizen also injects the params object into your view context automatically, so you have access to those objects as local variables (such as the url scope):

// article.jade

doctype html
html
  body
    main
      h1 #{title} - Page #{url.page}
      p#summary #{summary}
      #text #{text}

citizen sends HTML to the client by default, but you can also return JSON and JSONP with no extra work on your part.

You don't need a custom view just for JSON. You can tell a controller to return its content as plain text JSON by adding the format URL parameter, letting the same resource act as both a complete HTML view and JSON for AJAX requests and RESTful APIs.

http://www.cleverna.me/article/My-Clever-Article-Title/page/2/format/json

Returns...

{
  "title": "My Clever Article Title",
  "summary": "Am I not terribly clever?",
  "pages": {
    "1": "First page content",
    "2": "Second page content",
    "3": "Third page content"
  },
  "last modified": "2015 Mar 03"
}

Whatever you've added to the controller's emitter content object will be returned.

You can also specify specific top-level nodes to return instead of returning the entire content object by using the output URL parameter:

http://www.cleverna.me/article/My-Clever-Article-Title/page/2/format/json/output/pages

Returns...

{
  "1": "First page content",
  "2": "Second page content",
  "3": "Third page content"
}

Use dash notation in the output parameter to go deeper into the node tree:

http://www.cleverna.me/article/My-Clever-Article-Title/page/2/format/json/output/pages-2

Returns...

Second page content

The output parameter can be URL-encoded, allowing for dashes in your keys or whitespace characters if necessary:

http://www.cleverna.me/article/My-Clever-Article-Title/page/2/format/json/output/last%20modified

Returns...

2015 Mar 03

For JSONP, use format paired with callback in the URL:

http://www.cleverna.me/article/My-Clever-Article-Title/format/jsonp/callback/foo

Returns:

foo({
  "title": "My Clever Article Title",
  "summary": "Am I not terribly clever?",
  "pages": {
    "1": "First page content",
    "2": "Second page content",
    "3": "Third page content"
  },
  "last modified": "2015 Mar 03"
});

The output URL parameter works with JSONP as well.

http://www.cleverna.me/article/My-Clever-Article-Title/page/2/format/jsonp/callback/foo/output/pages-2

Returns...

foo("Second page content");

Do you always want a particular controller action to return JSON without the URL flag? Simple:

function handler(params, context, emitter) {

  // All requests handled by this controller action will output JSON
  params.route.format = 'json';

  emitter.emit('ready');
}

Are you building a RESTful API and want every request to return JSON? Also simple:

// File: /app/on/request.js
// All requests will be returned in JSON format because this function runs
// at the beginning of every request. Learn more in the "Application Events
// and the Context Argument" section.

function start(params, context, emitter) {
  params.route.format = 'json';
  emitter.emit('ready');
}

To remove whitespace from JSON or JSONP output, use the pretty config setting:

{
  "citizen": {
    "format": {
      "json": {
        "pretty": false
      },
      "jsonp": {
        "pretty": false
      }
    }
  }
}

By default, the server renders the view whose name matches that of the controller. To render a different view, use the view directive in your emitter.

To create custom error views for server errors, create a directory called /app/patterns/views/error and populate it with templates named after the HTTP response code or Node.js error code (or just use the scaffold utility to create your app, whose boilerplate includes some error templates to start with).

app/
  patterns/
    views/
      error/
        404.jade        // Handles 404 errors
        500.jade        // Handles 500 errors
        ENOENT.jade     // Handles bad file read operations
        error.jade      // Handles any error without its own template

These error views are only used when citizen is in production mode. In development and debug modes, citizen dumps the error directly.

In addition to the view content, the controller's ready emitter can also pass directives to render alternate views, set cookies and session variables, initiate redirects, call and render includes, cache views (or entire routes), and hand off the request to another controller for further processing.

By default, the server renders the view whose name matches that of the controller. To render a different view, use the view directive in your emitter:

// article controller

module.exports = {
  handler: handler
};

function handler(params, context, emitter) {
  var article = app.models.article.getArticle(params.url.article, params.url.page);

  emitter.emit('ready', {
    content: article,

    // This tells the server to render app/patterns/views/article/edit.jade
    view: 'edit'
  });
}

You set cookies by appending a cookie object to the emitter context.

Here's an example of a complete cookie object's default values:

cookie.foo = {
  value: '',
  // Valid expiration options are:
  // 'now' - deletes an existing cookie
  // 'never' - current time plus 30 years, so effectively never
  // 'session' - expires at the end of the browser session (default)
  // [time in milliseconds] - added to current time for a specific expiration date
  expires: 'session',

  // By default, a cookie's path is the same as the app path in your config
  path: app.config.citizen.urlPaths.app,

  // citizen's cookies are accessible via HTTP only by default. To access a
  // cookie via JavaScript, set this to false.
  httpOnly: true,

  // Cookies are insecure over HTTP. By default, they're made secure over HTTPS.
  // You can override that behavior globally with the https.secureCookies setting
  // in your config or on a case-by-case basis with this setting.
  secure: false
}

The following sample login controller tells the server to set username and passwordHash cookies that never expire:

// login controller

module.exports = {
  handler: handler
};

function handler(params, context, emitter) {
  var authenticate = app.models.login.authenticate({
        // Form values, just like URL parameters, are passed via the params
        // argument
        username: params.form.username,
        password: params.form.password
      }),
      // If a directive is an empty object, that's fine. citizen just ignores it.
      cookie = {};

  if ( authenticate.success ) {
    cookie = {
      // The cookie gets its name from the property name
      username: {
        value: authenticate.username,
        expires: 'never'
      },
      passwordHash: {
        value: authenticate.passwordHash,
        expires: 'never'
      }
    };
  }

  emitter.emit('ready', {
    content: {
      authenticate: authenticate
    },
    cookie: cookie
  });
};

Alternatively, you can pass the cookie directive strings, which will create cookies using the default attributes. The following code sets the same cookies, but they expire at the end of the browser session:

emitter.emit('ready', {
  cookie: {
    username: authenticate.username,
    passwordHash: authenticate.passwordHash
  }
});

Cookies sent by the client are available in params.cookie within the controller and simply cookie within the view context:

doctype html
html
  body
    #welcome
      if cookie.username
        | Welcome, #{cookie.username}.
      else
        a(href="/login") Login

Cookie variables you set within your controller aren't immediately available within the params.cookie scope. citizen's server has to receive the emitter's ready event from the controller before it can send the cookie to the client, so use a local instance of the variable if you need to access it during the same request.

If sessions are enabled, you can access session variables via params.session in your controller or simply session within views. These local scopes reference the current user's session without having to pass a session ID.

By default, a session has four properties: id, started, expires, and timer. The session ID is also sent to the client as a cookie called ctznSessionID.

Setting session variables is pretty much the same as setting cookie variables:

emitter.emit('ready', {
  session: {
    username: 'Danny',
    nickname: 'Doc'
  }
});

Sessions expire based on the sessionTimeout config property, which represents the length of a session in milliseconds. The default is 1200000 (20 minutes). The timer is reset with each request from the user. When the timer runs out, the session is deleted. Any client requests after that time will generate a new session ID and send a new session ID cookie to the client. Remember that the browser's session is separate from the server's session, so any cookies you've set with an expiration of session are untouched if the user's session expires on the server. You need to clear those cookies manually at the start of the next server session if you don't want them hanging around.

To forcibly clear and expire the current user's session:

emitter.emit('ready', {
  session: {
    expires: 'now'
  }
});

This won't end the session immediately. citizen has to receive your controller's response before it can act on this directive.

Like cookies, session variables you've just assigned aren't available during the same request within the params.session scope, so use a local instance if you need to access this data right away.

You can pass redirect instructions to the server that will be initiated after the request is complete. Redirects using this method within the controller are not immediate, so the controller will do everything it's been asked to do before the redirect is processed.

The redirect object takes a URL string in its shorthand version, or three options: statusCode, url, and refresh. If you don't provide a status code, citizen uses 302 (temporary redirect). The refresh option determines whether the redirect uses a Location header or the non-standard Refresh header.

// Initiate a temporary redirect using the Location header
emitter.emit('ready', {
  redirect: 'http://cleverna.me/login'
});

// Initiate a permanent redirect using the Refresh header, delaying the redirect
// by 5 seconds
emitter.emit('ready', {
  redirect: {
    url: 'http://cleverna.me/new-url',
    statusCode: 301,
    refresh: 5
  }
});

Unlike the Location header, if you use the refresh option, citizen will send a rendered view to the client because the redirect occurs client-side.

Using the Location header breaks (in my opinion) the Referer header because the Referer ends up being not the resource that initiated the redirect, but the resource prior to the page that initiated it. To get around this problem, citizen stores a session variable called ctznReferer that contains the URL of the resource that initiated the redirect, which you can use to redirect users properly. For example, if an unauthenticated user attempts to access a secure page and you redirect them to a login form, the address of the secure page will be stored in ctznReferer so you can send them there instead of the page containing the link to the secure page.

If you haven't enabled sessions, citizen falls back to creating a cookie named ctznReferer instead.

citizen lets you use complete MVC patterns as includes. These includes are more than just chunks of code that you can reuse because each has its own controller, model, and view(s). Here's the syntax:

function handler(params, context, emitter) {
  emitter.emit('ready', {
    include: {

      // The include name referenced in your view gets its name from the
      // property name. This include calls the _header controller with the
      // default action and view.
      header: {
        controller: '_header'
      },

      // This include calls the _footer controller using the "myAction" action.
      footer: {
        controller: '_footer',
        action: 'myAction'
      },

      // This include calls the _login-form controller using the "myAction"
      // action and "myView" view.
      loginForm: {
        controller: '_login-form',
        action: 'myAction',
        view: 'myView'
      },

      // This include calls the index controller, but processes it as if it
      // had been requested from a different URL. In this case, the include
      // will return JSON.
      index: {
        route: '/index/format/json/output/myNode'
      },

      // This include calls the index controller, but processes it as if it
      // had been requested from a different URL and uses an alternate view.
      index: {
        route: '/index/format/json/output/myNode',
        view: 'myView'
      }
    }
  });
}

Let's say our article pattern's Jade template has the following contents. The head section contains dynamic meta data, and the header nav's content changes depending on whether the user is logged in or not:

doctype html
html
  head
    title #{metaData.title}
    meta(name="description" content="#{metaData.description}")
    meta(name="keywords" content="#{metaData.keywords}")
    link(rel="stylesheet" type="text/css" href="app.css")
  body
    header
      a#logo Home page
      if cookie.username
        p Welcome, #{cookie.username}
      nav
        ul
          li
            a(href="/") Home
          li
            a(href="/articles") Articles
          if cookie.username
            li
              a(href="/admin") Site Administration
    main
      h1 #{title} - Page #{url.page}
      p#summary #{summary}
      #text #{text}

It probably makes sense to use includes for the head section and header because you'll use that code everywhere, but rather than simple partials, you can create citizen includes. The head section can use its own model for populating the meta data, and since the header is different for authenticated users, let's pull that logic out of the template and put it in the header's controller. I like to follow the convention of starting partials with an underscore, but that's up to you:

app/
  patterns/
    controllers/
      _head.js
      _header.js   // Doesn't pull data, so it doesn't need a model
      article.js
    models/
      _head.js
      article.js
    views/
      _head/
        _head.jade
      _header/
        _header.jade
        _header-authenticated.jade // A different header for logged in users
      article/
        article.jade

When the article controller is fired, it has to tell citizen which includes it needs. We do that with the include directive, which we pass via the context in the emitter:

// article controller

module.exports = {
  handler: handler
};

function handler(params, context, emitter) {
  var article = app.models.article.getArticle(params.url.article, params.url.page);

  emitter.emit('ready', {
    content: article,
    include: {
      head: {
        // If only the controller is specified, the default action handler() is
        // called and the default view is rendered (_head.jade in this case).
        controller: '_head'
      },
      header: {
        controller: '_header',
        // If the username cookie exists, use the authenticated action. If not,
        // use handler.
        action: params.cookie.username ? 'authenticated' : 'handler'

        // You can also specify the view here like this:
        // view: '_header-authenticated'
        // But we'll do that in the header controller instead. If you do specify
        // a view here, it will override whatever view is set in the controller.
      }
    }
  });
}

citizen include patterns have the same requirements as regular patterns, including a controller with a handler() function. The include directive above tells citizen to call the _head and _header controllers, pass them the same arguments that were passed to the article controller (params, context, and emitter), render their respective views, and add the resulting views to the view context.

Here's what our header controller looks like:

// _header controller

module.exports = {
  handler: handler,
  authenticated: authenticated
};

function handler(params, context, emitter) {
  emitter.emit('ready', {
    view: '_header'
  });
}

function authenticated(params, context, emitter) {
  emitter.emit('ready', {
    view: '_header-authenticated'
  });
}

And the header views:

// _header view (/patterns/views/_header/_header.jade)

header
  a#logo Home page
  nav
    ul
      li
        a(href="/") Home
      li
        a(href="/articles") Articles



// _header-authenticated view  (/patterns/views/_header/_header-authenticated.jade)

header
  a#logo Home page
  p Welcome, #{cookie.username}
  nav
    ul
      li
        a(href="/") Home
      li
        a(href="/articles") Articles
      li
        a(href="/admin") Site Administration

The rendered includes are stored in the include scope. The include object contains rendered HTML views, so you need to skip escaping (!= in Jade, {{{...}}} in Handlebars):

doctype html
html
  != include.head
  body
    != include.header
    main
      h1 #{title} - Page #{url.page}
      p#summary #{summary}
      #text #{text}

citizen includes are self-contained and sandboxed. Content you generate in the calling controller isn't passed to its include controllers, and content generated inside an include isn't passed back to the parent. citizen includes also can't make use of cookie, session, redirect, or handoff directives. They only use the content directive (to populate their own views), the view directive for setting the view used by the include, and the cache directive for controller caching.

A pattern meant to be used as an include can be accessed via HTTP just like any other controller. You could request the _head controller like so:

http://cleverna.me/_head

Perhaps you'd have it return meta data as JSON for the article pattern:

// http://cleverna.me/_head/article/My-Clever-Article-Title/format/json

{
  "metaData": {
    "title": "My Clever Article Title",
    "description": "My article's description.",
    "keywords": "clever, article, keywords"
  }
}

Of course, if you don't write the controller in a manner to accept direct requests and return content, it'll return nothing (or throw an error). When accessed via HTTP, the controller has access to all emitter directives.

Reminder: To make a controller private—inaccessible via HTTP, but accessible within your app—add a plus sign (+) to the beginning of the file name:

app/
  patterns/
    controllers/
      +_header.js  // Only accessible internally
      _head.js     // Accessible via www.cleverna.me/_head
      article.js   // Accessible via www.cleverna.me/article

citizen includes provide rich functionality, but they do have limitations and can be overkill in certain situations.

  • Do you only need to share a chunk of markup across different views? Use a standard Handlebars partial, Jade template, or HTML document. The syntax is easy and you don't have to create a full MVC pattern like you would with a citizen include.
  • Do you need to loop over a chunk of markup to render a data set? The server processes citizen includes and returns them as fully-rendered HTML (or JSON), not compiled templates. You can't loop over them and inject data like you can with Handlebars partials or Jade includes.
  • Do you need the ability to render different includes based on logic? citizen includes can have multiple views because they're full MVC patterns. Using a citizen include, you can call different actions and views based on logic and keep that logic in the controller where it belongs. Using Handlebars partials or Jade includes would require registering multiple partials and putting the logic in the view template.
  • Do you want the include to be accessible from the web? Since a citizen include has a controller, you can request it via HTTP like any other controller and get back HTML, JSON, or JSONP, which is great for AJAX requests and single page apps.

citizen allows the requested controller to give another controller the responsibility of handling the request and rendering its own view via a directive called handoff. The requested controller passes its content and directives to a secondary controller that assumes responsibility for the request, adding its own content and directives and rendering its own view. This is also a method for passing your own custom content and directives to the receiving controller.

A common use case for handoff would be to create a layout controller that serves as a template for every page on your site, rendering all the includes necessary and leaving only the core content and markup to the initially requested controller. Let's modify the article controller and view so it hands off rendering responsibility to a separate layout controller:

// article controller

module.exports = {
  handler: handler
};

function handler(params, context, emitter) {
  var article = app.models.article.getArticle(params.url.article, params.url.page);

  emitter.emit('ready', {
    content: article,
    handoff: {
      // Pass this request to app/patterns/controller/+_layout.js
      controller: '+_layout',

      // Specifying the action is optional. The layout controller will use its
      // default action, handler(), unless you specify a different action here.
      action: 'handler',

      // Specifying the view is optional. The layout controller will use its
      // default view unless you tell it to use a different one.
      view: '+_layout'
    },

    // A custom directive to drive some logic in the layout controller.
    // You could even pass a function here for layout to call.
    // Just don't use any reserved citizen directive names!
    myDirective: {
      doSomething: true
    }
  });
}

The view of the originally requested controller (article.jade in this case) is rendered and stored in the route.chain object:

// article.jade, which is stored in the route.chain scope

h1 #{title}
p#summary #{summary}
#text #{text}

The layout controller handles the includes, follows your custom directive, and renders its own view:

// layout controller

module.exports = {
  handler: handler
};

function handler(params, context, emitter) {
  // Access my custom directive using the context argument
  if ( context.myDirective && context.myDirective.doSomething ) {
    doSomething();
  }

  emitter.emit('ready', {
    // No need to specify previous directives, such as content, here because
    // citizen keeps track of the article pattern's request context throughout
    // the handoff process.
    include: {
      head: {
        controller: '_head'
      },
      header: {
        controller: '_header',
        action: params.cookie.username ? 'authenticated' : 'handler'
      }
    }
  });
}

function doSomething() {
  // do something
}

You can use handoff to chain requests across as many controllers as you want, with each controller's directives added to the request context. All controllers in the chain are stored in the route object as an array called route.chain:

[
  { controller: 'article',
    action: 'handler',
    view: 'article',
    viewContent: '<h1>My Article Title</h1><p id="summary">The article summary.</p><div id="text">The article text.</div>'
  },
  { controller: '+_layout',
    action: 'handler',
    view: '+_layout'
  }
]

You can loop over this object to render all the chained views:

// +_layout.jade

doctype html
html
  != include.head
  body
    != include.header
    main
      // Loop over each controller in the chain and incorporate its rendered view
      each controller in route.chain
        != controller.viewContent

It's assumed the last controller in the chain provides the master view, so it has no viewContent; that's what the server sends to the client.

citizen provides several ways for you to speed up your app's performance, most of which come at the cost of system resources (memory or CPU). You'll definitely want to do some performance monitoring to make sure the benefits are worth the cost.

Both dynamic routes and static assets can be served with gzip compression. To enable gzip compression for clients that support it:

{
  "citizen": {
    "gzip": {
      "enable": true
    }
  }
}

Proxies, firewalls, and other network circumstances can strip the request header that tells the server to use gzipped assets. You can force gzip for all clients like this:

{
  "citizen": {
    "gzip": {
      "enable": true,
      "force":  true
    }
  }
}

If you have route caching enabled, both the original and compressed version of the route will be cached, so your cache's memory utilization will increase.