High performance, high class web development for Node.js
npm:
$ npm install connect
$ npm install express
curl (or similar):
$ curl -# http://github.com/visionmedia/express/raw/master/install.sh | sh
git clone, first update the submodules:
$ git submodule update --init
$ make install
$ make install-support
The express.Server now inherits from http.Server, however follows the same idiom by providing express.createServer() as shown below. This means that you can utilize Express server's transparently with other libraries.
var app = require('express').createServer();
app.get('/', function(req, res){
res.send('hello world');
});
app.listen(3000);
Express supports arbitrary environments, such as production and development. Developers can use the configure() method to setup needs required by the current environment. When configure() is called without an environment name it will be run in every environment prior to the environment specific callback.
In the example below we only dumpExceptions, and respond with exception stack traces in development mode, however for both environments we utilize methodOverride and bodyDecoder.
app.configure(function(){
app.use('/', connect.methodOverride());
app.use('/', connect.bodyDecoder());
});
app.configure('development', function(){
app.use('/', connect.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use('/', connect.errorHandler());
});
For internal and arbitrary settings Express provides the set(key[, val]), enable(key), disable(key) methods:
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('views');
// => "... views directory ..."
app.enable('some feature');
// same as app.set('some feature', true);
app.disable('some feature');
// same as app.set('some feature', false);
});
To alter the environment we can set the CONNECT_ENV environment variable, or more specifically EXPRESS_ENV, for example:
$ EXPRESS_ENV=production node app.js
Express supports the following settings out of the box:
Express utilizes the HTTP verbs to provide a meaningful, expressive routing API. For example we may want to render a user's account for the path /user/12, this can be done by defining the route below. The values associated to the named placeholders, are passed as the third argument, which here we name params.
app.get('/user/:id', function(req, res, params){
res.send('user ' + params.id);
});
A route is simple a string which is compiled to a RegExp internally. For example when /user/:id is compiled, a simplified version of the regexp may look similar to:
\/user\/([^\/]+)\/?
Literal regular expressions may also be passed for complex uses:
app.get(/^\/foo(bar)?$/, function(){});
Below are some route examples, and the associated paths that they may consume:
"/user/:id"
/user/12
"/users/:id?"
/users/5
/users
"/files/*"
/files/jquery.js
/files/javascripts/jquery.js
"/file/*.*"
/files/jquery.js
/files/javascripts/jquery.js
"/user/:id/:operation?"
/user/1
/user/1/edit
"/products.:format"
/products.json
/products.xml
"/products.:format?"
/products.json
/products.xml
/products
We may pass control to the next matching route, by calling the fourth parameter, the next() function. When a match cannot be made, control is passed back to Connect.
app.get('/users/:id?', function(req, res, params){
if (params.id) {
// do something
} else {
next();
}
});
app.get('/users', function(req, res, params){
// do something else
});
The Express Plugin is no more! middleware via Connect can be passed to express.createServer() as you would with a regular Connect server. For example:
var connect = require('connect'),
express = require('express');
var app = express.createServer(
connect.logger(),
connect.bodyDecoder()
);
Alternatively we can use() them which is useful when adding middleware within configure() blocks:
app.use('/', connect.logger({ format: ':method :uri' }));
Express provides the app.error() method which receives exceptions thrown within a route, or passed to next(err). Below is an example which serves different pages based on our ad-hoc NotFound exception:
function NotFound(msg){
this.name = 'NotFound';
Error.call(this, msg);
Error.captureStackTrace(this, arguments.callee);
}
sys.inherits(NotFound, Error);
app.get('/404', function(req, res){
throw new NotFound;
});
app.get('/500', function(req, res){
throw new Error('keyboard cat!');
});
We can call app.error() several times as shown below. Here we check for an instanceof NotFound and show the 404 page, or we pass on to the next error handler.
app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.jade');
} else {
next(err);
}
});
Here we assume all errors as 500 for the simplicity of this demo, however you can choose whatever you like
app.error(function(err, req, res){
res.render('500.jade', {
locals: {
error: err
}
});
});
Our apps could also utilize the Connect errorHandler middleware to report on exceptions. For example if we wish to output exceptions in "development" mode to stderr we can use:
app.use('/', connect.errorHandler({ dumpExceptions: true }));
Also during development we may want fancy html pages to show exceptions that are passed or thrown, so we can set showStack to true:
app.use('/', connect.errorHandler({ showStack: true, dumpExceptions: true }));
The errorHandler middleware also responds with json if Accept: application/json is present, which is useful for developing apps that rely heavily on client-side JavaScript.
View filenames take the form Express.ENGINE, where ENGINE is the name of the module that will be required. For example the view layout.ejs will tell the view system to require('ejs'), the module being loaded must (currently) export the method exports.render(str, options) to comply with Express, however with will likely be extensible in the future.
Below is an example using Haml.js to render index.html, and since we do not use layout: false the rendered contents of index.html will be passed as the body local variable in layout.haml.
app.get('/', function(req, res){
res.render('index.haml', {
locals: { title: 'My Site' }
});
});
The Express view system has built-in support for partials and collections, which are sort of "mini" views representing a document fragment. For example rather than iterating in a view to display comments, we would use a partial with collection support:
partial('comment.haml', { collection: comments });
To make things even less verbose we can assume the extension as .haml when omitted, however if we wished we could use an ejs partial, within a haml view for example.
partial('comment', { collection: comments });
And once again even further, when rendering a collection we can simply pass an array, if no other options are desired:
partial('comments', comments);
Below are a few template engines commonly used with Express:
Get the case-insensitive request header key, with optional defaultValue:
req.header('Host');
req.header('host');
req.header('Accept', '*/*');
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.
// 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
Return the value of param name when present.
To utilize urlencoded request bodies, req.body should be an object. This can be done by using the connect.bodyDecoder middleware.
Queue flash msg of the given type.
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: [] }
Also aliased as req.xhr, this getter checks the X-Requested-With header to see if it was issued by an XMLHttpRequest:
req.xhr
req.isXMLHttpRequest
Get or set the response header key.
res.header('Content-Length');
// => undefined
res.header('Content-Length', 123);
// => 123
res.header('Content-Length');
// => 123
Sets the Content-Type response header to the given type.
var filename = 'path/to/image.png';
res.contentType(filename);
// res.headers['Content-Type'] is now "image/png"
Sets the Content-Disposition response header to "attachment", with optional filename.
res.attachment('path/to/my/image.png');
Used by res.download()
to transfer an arbitrary file.
res.sendfile('path/to/my.file');
NOTE: this is not a replacement for Connect's staticProvider middleware, nor does it perform any security checks, use with caution when using in a dynamic manor.
Transfer the given file as an attachment with optional alternative filename.
res.download('path/to/image.png');
res.download('path/to/image.png', 'foo.png');
This is equivalent to:
res.attachment(file);
res.sendfile(file);
The res.send()
method is a high level response utility allowing you to pass
objects to respond with json, strings for html, arbitrary _Buffer_s or numbers for status
code based responses. The following are all valid uses:
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);
By default the Content-Type response header is set, however if explicitly
assigned through res.send()
or previously with res.header()
or res.contentType()
it will not be set again.
Redirect to the given url with a default response status of 302.
res.redirect('/', 301);
res.redirect('/account');
res.redirect('http://google.com');
res.redirect('home');
res.redirect('back');
Express supports "redirect mapping", which by default provides home, and back. The back map checks the Referrer and Referer headers, while home utilizes the "home" setting and defaults to "/".
Apply an application level setting name to val, or get the value of name when val is not present:
app.set('reload views', 200);
app.set('reload views');
// => 200
Enable the given setting name:
app.enable('some arbitrary setting');
app.set('some arbitrary setting');
// => true
Disable the given setting name:
app.disable('some setting');
app.set('some setting');
// => false
Define a callback function for the given env (or all environments) with callback function:
app.configure(function(){
// executed for each env
});
app.configure('development', function(){
// executed for 'development' only
});
For use with res.redirect()
we can map redirects at the application level as shown below:
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');
});
Adds an error handler function which will receive the exception as the first parameter as shown below. Note that we may set several error handlers by making several calls to this method, however the handler should call next(err) if it does not wish to deal with the exception:
app.error(function(err, req, res, next){
res.send(err.message, 500);
});
Bind the app server to the given port, which defaults to 3000. When host is omitted all connections will be accepted via INADDR_ANY.
app.listen();
app.listen(3000);
app.listen(3000, 'n.n.n.n');
The port argument may also be a string representing the path to a unix domain socket:
app.listen('/tmp/express.sock');
Then try it out:
$ telnet /tmp/express.sock
GET / HTTP/1.1
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 11
Hello World