Getting Started With Express

In this short tutorial for Node Knockout we will be creating a small application using the popular Express framework.

Express is a light-weight Sinatra-inspired web development framework. Express provides several great features such as an intuitive view system, robust routing, an executable for generating applications and much more.

Installation

To get started with Express we first have to install it. There are several ways to do so, however my personal favourite is the following command which does not require a node package management system:

$ curl http://expressjs.com/install.sh | sh

Alternatively if we have npm installed we can simply execute:

$ npm install express

First Express Application

To create our first application we could use express(1) to generate an app for us, however an Express app can be a single JavaScript file if we wish, and in our case of a simple “Hello World” app that is exactly what we will do.

The first thing we need to do is require express, and create an app. The app variable shown below is an express.Server, however by convention we typically refer to Express servers as “apps”.

var express = require('express'),
    app = express.createServer();

Our next task is to set up one or more routes. A route consists of a path (string or regexp), callback function, and HTTP method. Our hello world example calls app.get() which represents the HTTP GET method, with the path “/”, representing our “root” page, followed by the callback function.

app.get('/', function(req, res){
    res.send('Hello World');
});

Next we need our server to listen on a given port. Below we call listen() which attempts to bind the server to port 3000 by default, however this can be whatever you like, for example listen(80).

app.listen();
console.log('Express server started on port %s', app.address().port);

We can execute the app simply by executing node(1) against our JavaScript file:

$ node app.js
Express server started on port 3000

Finally to confirm everything is working as expected:

$ curl http://localhost:3000
Hello World

Middleware

Behind the scenes the Connect middleware framework developed by myself (TJ Holowaychuk) and Tim Caswell is utilized to power the Express middleware. For example if we wish to add logging support to our hello world application, we can add the following line below app = express.createServer();:

app.use(express.logger());

For more information on middleware usage view the Middleware section of the Express Guide.

Source

Below is all 12 lines of source we used to create our first Express application:

var express = require('express'),
    app = express.createServer();

app.use(express.logger());

app.get('/', function(req, res){
    res.send('Hello World');
});

app.listen();
console.log('Express server started on port %s', app.address().port);

Express 1.0beta

Yesterday I released Express 1.0.0beta, a Sinatra inspired Node web development framework.

The beta is a near re-write consisting of roughly 300 commits, introducing new features, removing legacy code, and improving documentation. Express now runs on the Connect middleware framework, which replaces the previous concept of a Plugin. The performance of express has also been optimized and can now respond much faster than previous versions.

Connect

By abstracting the Express Plugin’s to a specific middleware framework, the community can now build on, and utilize this functionality to power their own frameworks. Connect currently provides:

  • bodyDecoder Buffers and parses json and urlencoded request bodies (extenable)
  • conditionalGet Provides 304
  • errorHandler Handles exceptions thrown, or passed through the stack
  • debug Outputs debugging console to all html responses
  • format Handles url path extensions or
  • gzip Compresses response bodies with gzip executable
  • lint Aids in middleware development
  • logger Provides common logger support, and custom log formats
  • methodOverride Provides faux HTTP method support via the _method param
  • responseTime Responds with the X-Response-Time header in milliseconds
  • compiler Supports arbitrary static compilation of files, currently supports less and sass.
  • cacheManifest Provides cache manifest for offline apps
  • jsonrpc Provides JSON-RPC 2.0 support
  • staticProvider Serves static files
  • cookieDecoder Provides cookie parsing support
  • session Provides session support
  • cache Provides memory caching
  • pubsub Publish subscribe messaging support
  • repl Read Evaluate Print Loop attached to
  • vhost Virtual host support

Performance Enhancements

Express is now much faster, with a concurrency of 80, and 8000 it can easily serve the typical “Hello World” response with ~10,000 requests per second on my machine. Ruby’s Thin (1.2.7) by comparison serves ~6500 rps. Finally by adding Sinatra in the mix I get ~1900 rps.

These numbers are to be taken lightly of course, and are simply relative, to re-cap:

Express        ~10,000
Rack/Thin      ~6,500
Rack/Sinatra   ~1,900

Graph for fun:

express vs sinatra

Documentation Overhaul

The new docs include a 1.x Migration Guide which should cover most of the changes that would be required to get you up and running. Another thing worth mentioning is that the docs are now generated via Markdown, and are located within ./docs which provides an easy way for developers to contribute docs.

I also have a Contrib Guide for those who are interested in helping Express out, and of course a completely revamped developers Guide.

For those who like to dig in deep and check out internals, my dox project generates the annotated source for Express.

Application Generating Executable

Express now ships with the express executable, creating app skeletons with a single tiny command:

$ express

thats it! of course view —help for more information.

Future of Express

From now on Express is clutter-free, no more dependencies on random extension libraries, no more high level http client library, just simply performance, routing, responding, configuration, and views. Feel free to post an issue if you find a bug, or have suggestions for focused features that you would like to see in the framework.

Looking forward to 1.0, Happy Express-ing everyone :)

Express vs Sinatra Benchmarks

So I have been setting up benchmark scripts for Express today, and so far some of the results have been quite interesting! The numbers shown should be taken lightly, however they consistently show that Express is quite fast.

If you are interested in benchmarking your own web applications you might want to read my last article ApacheBench Gnuplot Graphing Benchmarks.

All of the following benchmarks were generated using ApacheBench with a concurrency of 50 and performs 2000 requests. Keep in mind that Thin is used to serve Sinatra requests.

Express vs Sinatra

For those who dont know Express is a NodeJS framework inspired by Ruby’s Sinatra. Below we have the benchmark results for a typical “Hello World” response, which include nodejs benchmarks without the overhead of features provided by Express:

express vs sinatraexpress sinatra requests per second

Haml.js vs Ruby Haml

Next up is my JavaScript Haml implementation. Below are the results of running Express & haml.js vs Sinatra & Haml. Both serve a layout template, as well as a page specific template.

javascript haml vs ruby hamlrequests per second

Sass.js vs Ruby Sass

Finally we have JavaScript Sass vs the regular Ruby implementation packaged with haml. Both implementations serve the same 80 line stylesheet.

sass js vs ruby sasssass benchmarks

Stay tuned for more benchmarks!

Express 0.9.0 released

A few minutes ago Express was released, packed with tons of bug fixes and features.

Installation

As always Express can be installed via the Kiwi package manager for NodeJS using one simple command:

$ kiwi install express

Alternatively you can Download a tarball, or install via Git:

 $ git clone git://github.com/visionmedia/express.git
 $ cd express && git submodule update --init

New Haml Support

I wrote another JavaScript implementation of Haml simply because haml-js was a little clunky and did not comply with the original Haml implementation. My implementation is about 4 times faster (not that it really matters once caching is involved), and more compliant.

“max upload size” setting

We added a simple “max upload size” setting which checks multipart post body Content-Length and raises an error when the specified size is exceeded.

set('max upload size', (5).megabytes)

Request#render() callbacks

The Request#render() method now accepts a trailing callback function, which passes an exception (if a problem occurs), and the final view rendered.

self.render('user.html.haml', function(err, contents){
  // perform caching etc
})

Map Route Parameters With param()

The param() DSL-level function allows mapping of parameter names to preprocessor functions. In the example shown below we want to prevent users from typing an invalid path such as /user/rawr, as we need a user id. Our function passed to param() accepts the path segment, which when returning false will be disregarded as a valid route, and the router will continue searching for a matching route. Otherwise if parseInt() succeeds the id variable passed to our route is now a Number and not a String.

param('id', function(val){
  val = parseInt(val)
  return isNaN(val) ? false : val
})

get('/user/:id', function(id){
  return 'User ' + id
})

In the near future I plan on having parameter preprocessing async-friendly, so that for example :user_id could be used to load an User record from the database.

Unified Exception Handling With error()

The error() DSL-level function is passed the exception thrown. Keep in mind that with async environments like node we loose context if an exception bubbles, so we pass them to Request#error() to allow for the unified error handler shown below.

error(function(err){
  this.contentType('html')
  this.halt(200, '<p><strong>Error:</strong> ' + err.message + '</p>')
})

Handling Missing Pages With notFound()

The request level method Request#notFound() is now used in place where you would normally call halt(404), and supports deferring to the DSL-level function notFound() as shown below:

notFound(function(){
  this.halt(404, 'your lame')
})

Changelog

  • Added DSL level error() route support
  • Added DSL level notFound() route support
  • Added Request#error()
  • Added Request#notFound()
  • Added Request#render() callback function. Closes #258
  • Added “max upload size” setting
  • Added “magic” variables to collection partials (__index__, __length__, __isFirst__, __isLast__). Closes #254
  • Added haml.js submodule; removed haml-js
  • Added callback function support to Request#halt() as 3rd/4th arg
  • Added preprocessing of route param wildcards using param(). Closes #251
  • Added view partial support (with collections etc)
  • Fixed bug preventing falsey params (such as ?page=0). Closes #286
  • Fixed setting of multiple cookies. Closes #199
  • Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)
  • Changed; session cookie is now httpOnly
  • Changed; Request is no longer global
  • Changed; Event is no longer global
  • Changed; “sys” module is no longer global
  • Changed; moved Request#download to Static plugin where it belongs
  • Changed; Request instance created before body parsing. Closes #262
  • Changed; Pre-caching views in memory when “cache view contents” is enabled. Closes #253
  • Changed; Pre-caching view partials in memory when “cache view partials” is enabled
  • Updated support to node —version 0.1.90
  • Updated dependencies
  • Removed set(“session cookie”) in favour of use(Session, { cookie: { … }})
  • Removed utils.mixin(); use Object#mergeDeep()