Express

High performance web framework for node.

index

lib/express/index.js

Framework version.

exports.version = '1.0.0beta';

Module dependencies.

var 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.path[name] !== undefined) {
        return this.params.path[name]; 
    }
    // Query string params
    if (this.params.get[name] !== undefined) {
        return this.params.get[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 given file. Automatically sets the Content-Type response header, and responds with 404 / 500 appropriately.

  • param: String file

  • api: public

http.ServerResponse.prototype.sendfile = function(file){
    var self = this;
    fs.readFile(file, function(err, buf){
        if (err) {
            self.send(err.errno === process.ENOENT
                ? 404
                : 500);
        } else {
            self.contentType(file);
            self.send(buf);
        }
    });
};

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 given file with optional filename as an attachment.

  • param: String file

  • param: String filename

  • return: Type

  • api: public

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

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, params){
     return '/post/' + 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, this.req.params.path)
        : map[url];

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

server

lib/express/server.js

Module dependencies.

var sys = require('sys'),
    url = require('url'),
    view = require('./view'),
    connect = require('connect'),
    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 = {};
    connect.Server.call(this, middleware || []);

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

    // Use router, expose as app.get(), etc
    this.use('/', router(function(app){
        self.routes = app;
    }));
};

Inherit from connect.Server.

sys.inherits(Server, connect.Server);

Start listening on the given port / host.

  • param: Number port

  • param: String host

  • api: public

Server.prototype.listen = function(port, host){
    this.set('env', process.env.EXPRESS_ENV || process.connectEnv.name);
    this.runConfig('any', this.set('env'));

    // Setup view reloading
    if (this.set('reload views')) {
        view.watcher.call(this, this.set('reload views'));
    }

    connect.Server.prototype.listen.call(this, port, host);
};

Assign a custom exception handler callback fn.

  • param: Function fn

  • return: Server for chaining

  • api: public

Server.prototype.error = function(fn){
    this.use('/', function(err, req, res, next){
        fn.apply(this, arguments);
    });
    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;
};

Disable setting.

  • param: String setting

  • return: Server for chaining

  • api: public

Server.prototype.configure = function(env, fn){
    if (typeof env === 'function') fn = env, env = 'any';
    (this.config[env] = this.config[env] || []).push(fn);
    return this;
};

// Generate routing methods

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

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, '>');
};

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 = {};

Clear the view cache.

  • api: public

exports.clearCache = function(){
    viewCache = {};
};

View helpers merged with each render's locals.

  • type: Object

var helpers = exports.helpers = {};

Render view partial with the given options.

Options

  • 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){
    if (ext &amp;&amp; view.indexOf('.') &lt; 0) {
        view += ext;
    }
    if (options instanceof Array) {
        options = { collection: options };
    }
    options = options || {};
    options.partial = true;
    options.layout = false;
    var collection = options.collection;
    if (collection) {
        var name = options.as || view.split('.')[0],
            len = collection.length;
        var locals = options.locals = options.locals || {};
        locals.collectionLength = len;
        return collection.map(function(val, i){
            if (typeof name === 'string') {
                locals[name] = val;
            } else if (name === global) {
                utils.merge(locals, val);
            } else {
                options.context = val;
            }
            locals.firstInCollection = i === 0;
            locals.indexInCollection = i;
            locals.lastInCollection = i === len - 1;
            return this.render(view, options);
        }, this).join('');
    } else {
        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 Template evaluation context (this)
  • locals Object containing local variables
  • filename Filename used for the cache option
  • cache Cache intermediate JavaScript in memory
  • debug Output debugging information

  • param: String view

  • param: Object options

  • param: Function fn

  • api: public

http.ServerResponse.prototype.render = function(view, options, fn){
    options = options || {};
    var defaultEngine = this.app.set('view engine');

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

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

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

    // Partials support
    if (options.partial) {
        root += '/partials';
    }

    // View path
    var path = root + '/' + view;

    // Send response
    function send(str) {
        self.writeHead(200, {
            'Content-Type': 'text/html',
            'Content-Length': str.length
        });
        self.end(str);
    }

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

    // 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 {
            throw err;
        }
    }

    // Cache contents
    try {
        var str = viewCache[path] || cacheView(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;
        self.render(layout, options);
    } else if (partial) {
        return str;
    } else if (fn) {
        fn(null, str);
    } else {
        send(str);
    }
};