Express

High performance web framework for node.

index

lib/express/index.js

Re-export connect auto-loaders.

This prevents the need to require('connect') in order to access core middleware, so for example express.logger() instead of require('connect').logger().

var exports = module.exports = require('connect').middleware;

Framework version.

exports.version = '1.0.0rc';

Module dependencies.

var Server = exports.Server = require('./server');

Shortcut for new Server(...).

  • param: Function ...

  • return: Server

  • api: public

exports.createServer = function(){
    return new Server(Array.prototype.slice.call(arguments));
};

View extensions.

require('./view');
require('./request');
require('./response');

request

lib/express/request.js

Module dependencies.

var http = require('http'),
    utils = require('./utils'),
    mime = require('connect/utils').mime;

Return request header or optional default.

Examples

req.header('Content-Type');
// => "text/plain"

req.header('content-type');
// => "text/plain"

req.header('Accept');
// => undefined

req.header('Accept', 'text/html');
// => "text/html"

  • param: String name

  • param: String defaultValue

  • return: String

  • api: public

http.IncomingMessage.prototype.header = function(name, defaultValue){
    return this.headers[name.toLowerCase()] || defaultValue;
};

Check if the Accept header is present, and includes the given type.

When the Accept header is not present true is returned. Otherwise the given type is matched by an exact match, and then subtypes. You may pass the subtype such as "html" which is then converted internally to "text/html" using the mime lookup table.

Examples

// Accept: text/html
req.accepts('html');
// => true

// Accept: text/*; application/json
req.accepts('html');
req.accepts('text/html');
req.accepts('text/plain');
req.accepts('application/json');
// => true

req.accepts('image/png');
req.accepts('png');
// => false

  • param: String type

  • return: Boolean

  • api: public

http.IncomingMessage.prototype.accepts = function(type){
    var accept = this.header('Accept');
    if (!accept || accept === '*

') { return true; } else if (type) { // Allow "html" vs "text/html" etc if (type.indexOf('/') < 0) { type = mime.types['.' + type]; } // Check if we have a direct match if (accept.indexOf(type) >= 0) { return true; // Check if we have type/ } else { type = type.split('/')[0] + '/'; return accept.indexOf(type) >= 0; } } else { return false; } };

/** Return the value of param name when present.

  • Checks route placeholders, ex: /user/:id
  • Checks query string params, ex: ?id=12
  • Checks urlencoded body params, ex: id=12

To utilize urlencoded request bodies, req.body should be an object. This can be done by using the connect.bodyDecoder middleware.

  • param: String name

  • return: String

  • api: public

http.IncomingMessage.prototype.param = function(name){
    // Route params like /user/:id
    if (this.params[name] !== undefined) {
        return this.params[name]; 
    }
    // Query string params
    if (this.query[name] !== undefined) {
        return this.query[name]; 
    }
    // Request body params via connect.bodyDecoder
    if (this.body &amp;&amp; this.body[name] !== undefined) {
        return this.body[name];
    }
};

Queue flash msg of the given type.

Examples

 req.flash('info', 'email sent');
 req.flash('error', 'email delivery failed');
 req.flash('info', 'email re-sent');
 // => 2

 req.flash('info');
 // => ['email sent', 'email re-sent']

 req.flash('info');
 // => []

 req.flash();
 // => { error: ['email delivery failed'], info: [] }

  • param: String type

  • param: String msg

  • return: Array | Object | Number

  • api: public

http.IncomingMessage.prototype.flash = function(type, msg){
    var msgs = this.session.flash = this.session.flash || {};
    if (type &amp;&amp; msg) {
        msg = utils.miniMarkdown(utils.htmlEscape(msg));
        return (msgs[type] = msgs[type] || []).push(msg);
    } else if (type) {
        var arr = msgs[type];
        delete msgs[type];
        return arr || [];
    } else {
        this.session.flash = {};
        return msgs;
    }
};

// Callback for isXMLHttpRequest / xhr

function isxhr() {
    return this.header('X-Requested-With', '').toLowerCase() === 'xmlhttprequest';
}

Check if the request was an XMLHttpRequest.

  • return: Boolean

  • api: public

http.IncomingMessage.prototype.__defineGetter__('isXMLHttpRequest', isxhr);
http.IncomingMessage.prototype.__defineGetter__('xhr', isxhr);

response

lib/express/response.js

Module dependencies.

var fs = require('fs'),
    http = require('http'),
    path = require('path'),
    utils = require('connect/utils'),
    mime = require('connect/utils').mime,
    Buffer = require('buffer').Buffer;

Send a response with the given body and optional headers and status code.

Examples

res.send(new Buffer('wahoo'));
res.send({ some: 'json' });
res.send('<p>some html</p>');
res.send('Sorry, cant find that', 404);
res.send('text', { 'Content-Type': 'text/plain' }, 201);
res.send(404);

  • param: String | Object | Number | Buffer body or status

  • param: Object | Number headers or status

  • param: Number status

  • return: ServerResponse

  • api: public

http.ServerResponse.prototype.send = function(body, headers, status){
    // Allow status as second arg
    if (typeof headers === 'number') {
        status = headers,
        headers = null;
    }

    // Defaults
    status = status || 200;
    headers = headers || {};

    // Determine content type
    switch (typeof body) {
        case 'number':
            if (!this.headers['Content-Type']) {
                this.contentType('.txt');
            }
            body = http.STATUS_CODES[status = body];
            break;
        case 'string':
            if (!this.headers['Content-Type']) {
                this.contentType('.html');
            }
            break;
        case 'object':
            if (body instanceof Buffer) {
                if (!this.headers['Content-Type']) {
                    this.contentType('.bin');
                }
            } else {
                if (!this.headers['Content-Type']) {
                    this.contentType('.json');
                }
                body = JSON.stringify(body);
            }
            break;
    }

    // Populate Content-Length
    if (!this.headers['Content-Length']) {
        this.header('Content-Length', body instanceof Buffer
            ? body.length
            : Buffer.byteLength(body));
    }

    // Merge headers passed
    utils.merge(this.headers, headers);

    // Respond
    this.writeHead(status, this.headers);
    this.end(body);
};

Transfer the file at the given path. Automatically sets the Content-Type response header via res.contentType().

The given callback fn is invoked when an error occurs, passing it as the first argument, or when the file is transferred, passing the path as the second argument.

  • param: String path

  • param: Function fn

  • api: public

http.ServerResponse.prototype.sendfile = function(path, fn){
    var self = this;
    fs.readFile(path, function(err, buf){
        if (err) {
            if (fn) {
                fn(err, path);
            } else {
                self.req.next(err);
            }
        } else {
            self.contentType(path);
            self.send(buf);
            fn &amp;&amp; fn(null, path);
        }
    });
};

Set Content-Type response header passed through mime.type().

Examples

var filename = 'path/to/image.png';
res.contentType(filename);
// res.headers['Content-Type'] is now "image/png"

  • param: String type

  • return: String the resolved mime type

  • api: public

http.ServerResponse.prototype.contentType = function(type){
    return this.header('Content-Type', mime.type(type));
};

Set Content-Disposition header to attachment with optional filename.

  • param: String filename

  • return: ServerResponse

  • api: public

http.ServerResponse.prototype.attachment = function(filename){
    this.header('Content-Disposition', filename
        ? 'attachment; filename="' + path.basename(filename) + '"'
        : 'attachment');
    return this;
};

Transfer the file at the given path, with optional filename as an attachment. Once transferred, or if an error occurs fn is called with the error and path.

  • param: String path

  • param: String filename

  • param: Function fn

  • return: Type

  • api: public

http.ServerResponse.prototype.download = function(path, filename, fn){
    this.attachment(filename || path).sendfile(path, fn);
};

Set or get response header name with optional val.

  • param: String name

  • param: String val

  • return: String

  • api: public

http.ServerResponse.prototype.header = function(name, val){
    return val === undefined
        ? this.headers[name]
        : this.headers[name] = val;
};

Redirect to the given url with optional response status defauling to 302.

The given url can also be the name of a mapped url, for example by default express supports "back" which redirects to the Referrer or Referer headers or the application's "home" setting. Express also supports "home" out of the box, which can be set via app.set('home', '/blog');, and defaults to '/'.

Redirect Mapping

To extend the redirect mapping capabilities that Express provides, we may use the app.redirect() method:

app.redirect('google', 'http://google.com');

Now in a route we may call:

res.redirect('google');

We may also map dynamic redirects:

 app.redirect('comments', function(req, res){
     return '/post/' + req.params.id + '/comments';
 });

So now we may do the following, and the redirect will dynamically adjust to the context of the request. If we called this route with GET /post/12 our redirect Location would be /post/12/comments.

 app.get('/post/:id', function(req, res){
     res.redirect('comments');
 });

  • param: String url

  • param: Number code

  • api: public

http.ServerResponse.prototype.redirect = function(url, status){
    var basePath = this.app.set('home') || '/';

    // Setup redirect map
    var map = {
        back: this.req.headers.referrer || this.req.headers.referer || basePath,
        home: basePath
    };

    // Support custom redirect map
    map.__proto__ = this.app.redirects;

    // Attempt mapped redirect
    var mapped = typeof map[url] === 'function'
        ? map[url](this.req, this)
        : map[url];

    // Perform redirect
    this.writeHead(status || 302, { 'Location': mapped || url });
    this.end();
};

utils

lib/express/utils.js

Parse mini markdown implementation. The following conversions are supported, primarily for the "flash" middleware:

foo or foo become <em>foo</em> foo or foo become <strong>foo</strong> A becomes <a href="B">A</a>

  • param: String str

  • return: String

  • api: public

exports.miniMarkdown = function(str){
    return String(str)
        .replace(/(__|\*\*)(.*?)\1/g, '<strong>$2</strong>')
        .replace(/(_|\*)(.*?)\1/g, '<em>$2</em>')
        .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
};

Escape special characters in the given string of html.

  • param: String html

  • return: String

  • api: public

exports.htmlEscape = function(html) {
    return String(html)
        .replace(/&/g, '&')
        .replace(/"/g, '"')
        .replace(/</g, '<')
        .replace(/>/g, '>');
};

server

lib/express/server.js

Module dependencies.

var sys = require('sys'),
    url = require('url'),
    view = require('./view'),
    connect = require('connect'),
    utils = require('connect/utils'),
    queryString = require('querystring'),
    router = require('connect/middleware/router');

Initialize a new Server with optional middleware.

  • param: Array middleware

  • api: public

var Server = exports = module.exports = function Server(middleware){
    var self = this;
    this.config = {};
    this.settings = {};
    this.redirects = {};
    this.viewHelpers = {};
    this.dynamicViewHelpers = {};
    this.errorHandlers = [];
    connect.Server.call(this, middleware || []);

    // Default "home" to / 
    this.set('home', '/');

    // Set "env" to EXPRESS_ENV or connectEnv.name
    this.set('env',
        process.env.EXPRESS_ENV ||
        process.connectEnv.name);

    // Expose objects to each other
    this.use(function(req, res, next){
        req.query = {};
        res.headers = {};
        req.app = res.app = self;
        req.res = res;
        res.req = req;
        req.next = next;
        // Assign req.params.get
        if (req.url.indexOf('?') &gt; 0) {
            var query = url.parse(req.url).query;
            req.query = queryString.parse(query);
        }
        next();
    });

    // Use router, expose as app.get(), etc
    var fn = router(function(app){ self.routes = app; });
    this.__defineGetter__('router', function(){
        this.__usedRouter = true;
        return fn;
    });
};

Inherit from connect.Server.

sys.inherits(Server, connect.Server);

Stack error handler callbacks, then listen on the given port and host.

  • param: Number | String port

  • param: String host

  • return: Type

  • api: public

Server.prototype.listen = function(){
    this.errorHandlers.forEach(function(fn){
        this.use(function(err, req, res, next){
            fn.apply(this, arguments);
        });
    }, this);
    connect.Server.prototype.listen.apply(this, arguments);
};

Proxy connect.Server#use() to apply settings to mounted applications.

  • param: String | Function | Server route

  • param: Function | Server middleware

  • return: Server for chaining

  • api: public

Server.prototype.use = function(route, middleware){
    if (typeof route !== 'string') {
        middleware = route, route = '/';
    }

    connect.Server.prototype.use.call(this, route, middleware);

    // Mounted an app
    if (middleware instanceof Server) {
        // Home is /:route/:home
        var app = middleware,
            home = app.set('home');
        if (home === '/') home = '';
        app.set('home', (app.route || '') + home);
        // Mounted hook
        if (app.__mounted) app.__mounted.call(app, this);
    }

    return this;
};

Assign a callback fn which is called when this Server is passed to Server#use().

Examples

var app = express.createServer(), blog = express.createServer();

blog.mounted(function(parent){ // parent is app // "this" is blog });

app.use(blog);

  • param: Function fn

  • return: Server for chaining

  • api: public

Server.prototype.mounted = function(fn){
    this.__mounted = fn;
    return this;
};

See view.register.

  • return: Server for chaining

  • api: public

Server.prototype.register = function(){
    view.register.apply(this, arguments);
    return this;
};

Register the given view helpers obj. This method can be called several times to apply additional helpers.

  • param: Object obj

  • return: Server for chaining

  • api: public

Server.prototype.helpers = function(obj){
    utils.merge(this.viewHelpers, obj);
    return this;
};

Register the given dynamic view helpers obj. This method can be called several times to apply additional helpers.

  • param: Object obj

  • return: Server for chaining

  • api: public

Server.prototype.dynamicHelpers = function(obj){
    utils.merge(this.dynamicViewHelpers, obj);
    return this;
};

Assign a custom exception handler callback fn. These handlers are always last in the middleware stack.

  • param: Function fn

  • return: Server for chaining

  • api: public

Server.prototype.error = function(fn){
    this.errorHandlers.push(fn);
    return this;
};

Assign setting to val, or return setting's value.

  • param: String setting

  • param: String val

  • return: Server | Mixed for chaining, or the setting value

  • api: public

Server.prototype.set = function(setting, val){
    if (val === undefined) {
        return this.settings[setting];
    } else {
        this.settings[setting] = val;
        return this;
    }
};

Enable setting.

  • param: String setting

  • return: Server for chaining

  • api: public

Server.prototype.enable = function(setting){
    return this.set(setting, true);
};

Disable setting.

  • param: String setting

  • return: Server for chaining

  • api: public

Server.prototype.disable = function(setting){
    return this.set(setting, false);
};

Redirect key to url.

  • param: String key

  • param: String url

  • return: Server for chaining

  • api: public

Server.prototype.redirect = function(key, url){
    this.redirects[key] = url;
    return this;
};

Configure callback for the given env.

  • param: String env

  • param: Function fn

  • return: Server for chaining

  • api: public

Server.prototype.configure = function(env, fn){
    if (typeof env === 'function') {
        fn = env, env = 'all';
    }
    if (env === 'all' || this.set('env') === env) {
        fn.call(this);
    }
    return this;
};

// Generate routing methods

(function(method){
    Server.prototype[method] = function(path, fn){
        if (!this.__usedRouter) {
            this.use(this.router);
        }
        this.routes[method](path, fn);
        return this;
    };
    return arguments.callee;
})('get')('post')('put')('del');

view

lib/express/view.js

Module dependencies.

var extname = require('path').extname,
    mime = require('connect/utils').mime,
    utils = require('connect/utils'),
    http = require('http'),
    sys = require('sys'),
    fs = require('fs');

Cache supported template engine exports to increase performance by lowering the number of calls to require().

  • type: Object

var cache = {};

Cache view contents to prevent I/O hits.

  • type: Object

var viewCache = {};

Register the given template engine exports as ext. For example we may wish to map ".html" files to jade:

app.register('.html', require('jade'));

This is also useful for libraries that may not match extensions correctly. For example my haml.js library is installed from npm as "hamljs" so instead of layout.hamljs, we can register the engine as ".haml":

app.register('.haml', require('haml-js'));

For engines that do not comply with the Express specification, we can also wrap their api this way.

app.register('.foo', { render: function(str, options) { // perhaps their api is // foo.toHTML(str, options); } });

  • param: String ext

  • param: Object obj

  • api: public

exports.register = function(ext, exports) {
    cache[ext] = exports;
};

Render view partial with the given options.

Options

  • object Single object with name derived from the view (unless as is present)

  • as Variable name for each collection value, defaults to the view name.

    • as: 'something' will add the something local variable
    • as: this will use the collection value as the template context
    • as: global will merge the collection value's properties with locals

  • collection Array of objects, the name is derived from the view name itself. For example video.html will have a object video available to it.

  • param: String view

  • param: Object | Array options or collection

  • return: String

  • api: public

http.ServerResponse.prototype.partial = function(view, options, ext){
    // Inherit parent view extension when not present
    if (ext &amp;&amp; view.indexOf('.') &lt; 0) {
        view += ext;
    }

    // Allow collection to be passed as second param
    if (Array.isArray(options)) {
        options = { collection: options };
    }

    // Defaults
    options = options || {};
    options.locals = options.locals || {};
    options.partial = true;
    options.layout = false;

    // Collection support
    var collection = options.collection;
    if (collection) {
        var name = options.as || view.split('.')[0],
            len = collection.length;
        delete options.collection;
        options.locals.collectionLength = len;
        return collection.map(function(val, i){
            options.locals.firstInCollection = i === 0;
            options.locals.indexInCollection = i;
            options.locals.lastInCollection = i === len - 1;
            options.object = val;
            return this.partial(view, options);
        }, this).join('');
    } else {
        if (options.object) {
            var name = options.as || view.split('.')[0];
            if (typeof name === 'string') {
                options.locals[name] = options.object;
            } else if (name === global) {
                utils.merge(options.locals, options.object);
            } else {
                options.context = options.object;
            }
        }
        return this.render(view, options);
    }
};

Render view with the given options and optional callback fn. When a callback function is given a response will not be made automatically, however otherwise a response of 200 and text/html is given.

Options

Most engines accept one or more of the following options, both haml and jade accept all:

  • context|scope Template evaluation context (this)
  • locals Object containing local variables
  • debug Output debugging information

  • param: String view

  • param: Object | Function options or callback function

  • param: Function fn

  • api: public

http.ServerResponse.prototype.render = function(view, options, fn){
    // Support callback function as second arg
    if (typeof options === 'function') {
        fn = options, options = {};
    }
    
    var options = options || {},
        viewOptions = this.app.set('view options'),
        defaultEngine = this.app.set('view engine');

    // Mixin "view options"
    if (viewOptions) options.__proto__ = viewOptions;

    // Support "view engine" setting
    if (view.indexOf('.') &lt; 0 &amp;&amp; defaultEngine) {
        view += '.' + defaultEngine;
    }

    // Defaults
    var self = this,
        helpers = this.app.viewHelpers,
        dynamicHelpers = this.app.dynamicViewHelpers,
        root = viewRoot(this.app),
        ext = extname(view),
        partial = options.partial,
        layout = options.layout === undefined ? true : options.layout,
        layout = layout === true
            ? 'layout' + ext
            : layout;

    // Allow layout name without extension
    if (typeof layout === 'string' &amp;&amp; layout.indexOf('.') &lt; 0) {
        layout += ext;
    }

    // Normalize "context" to "scope", defaulting to this response
    options.scope = options.scope || options.context || this;

    // Auto-cache in production
    if (this.app.set('env') === 'production') {
        options.cache = true;
    }

    // Partials support
    if (options.partial) {
        root = this.app.set('partials')
            ? this.app.set('partials')
            : root + '/partials';
    }

    // View path
    var path = view[0] === '/'
        ? view
        : root + '/' + view;

    // Pass filename to the engine and view
    options.locals = options.locals || {};
    options.locals.__filename = options.filename = path;

    // Dynamic helper support
    if (options.dynamicHelpers !== false) {
        var keys = Object.keys(dynamicHelpers);
        for (var i = 0, len = keys.length; i &lt; len; ++i) {
            var key = keys[i],
                val = dynamicHelpers[key];
            if (typeof val === 'function') {
                helpers[key] = val.call(
                    this.app, 
                    this.req, 
                    this);
            }
        }
    }

    // Always expose partial() as a local
    options.locals.partial = function(view, options){
        return self.partial.call(self, view, options, ext);
    };
    
    // Merge view helpers
    options.locals.__proto__ = helpers;

    function error(err) {
        if (fn) {
            fn(err);
        } else {
            self.req.next(err);
        }
    }

    // Cache contents
    try {
        var str = (options.cache ? viewCache[path] : null) || cacheViewSync(path);
    } catch (err) {
        return error(err);
    }

    // Cache template engine exports
    var engine = cache[ext] || (cache[ext] = require(ext.substr(1)));

    // Attempt render
    try {
        var str = engine.render(str, options);
    } catch (err) {
        return error(err);
    }

    // Layout support
    if (layout) {
        options.layout = false;
        options.locals.body = str;
        options.isLayout = true;
        self.render(layout, options, fn);
    } else if (partial) {
        return str;
    } else if (fn) {
        fn(null, str);
    } else {
        self.send(str);
    }
};