Create a Web App with Node.js
Node.js is the new hype for server-side webish things. It's evented, non-blocking, fast and in javascript (not another language to learn, yay!). Real-Time Web is around the corner.
Javascript means you can use existing JS librairies. In this example I will use Mustache.js and Underscore to create the core of a minimal web application.
Let's begin
I have 4 files: main.js, mustache.js, underscore.js, templates/index.html
We will put our code in main.js. First we require the things we'll need:
var sys = require('sys'), http = require('http'), posix = require('posix')
require('./underscore')
var Mustache = require('./mustache')
Note that Underscore is CommonJS compliant so you don't have to do anything for it. Then we add our actions which are composed by 3 things: URI, template name and the view.
var actions = [];
actions.push({
path: "/",
template: "index.html",
view: {
title: "Joe",
calc: function() { return 2 + 4; }
}
})
We can create as many actions as we want. Now let's create the server:
http.createServer(function (req, res) {}).listen(8000);
The server will be running but will not know how not handle actions. Let's add a request listener:
http.createServer(function (req, res) {
req.addListener('complete', function() {})
}).listen(8000);
The req variable contains the URI, so we will select the action corresponding to the request:
http.createServer(function (req, res) {
req.addListener('complete', function() {
var action = _(actions).chain().select(function(a) { return req.uri.path == a.path }).first().value()
})
}).listen(8000);
If the request doesn't match an action, we will send an error:
http.createServer(function (req, res) {
req.addListener('complete', function() {
var action = _(actions).chain().select(function(a) { return req.uri.path == a.path }).first().value()
if (_.isEmpty(action)) {
res.sendHeader(404, {'Content-Type': 'text/plain'})
res.sendBody("Error")
res.finish()
} else {}
})
}).listen(8000);
In the other case we render the action:
http.createServer(function (req, res) {
req.addListener('complete', function() {
var action = _(actions).chain().select(function(a) { return req.uri.path == a.path }).first().value()
if (_.isEmpty(action)) {
res.sendHeader(404, {'Content-Type': 'text/plain'})
res.sendBody("Error")
res.finish()
} else {
posix.cat("./templates/" + action.template).addCallback(function(template) {
res.sendHeader(200, {'Content-Type': 'text/html'})
res.sendBody(Mustache.to_html(template, action.view))
res.finish()
})
}
})
}).listen(8000);
We get the template content then renders it when the I/O process is complete. Here's the template:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>My Framework</title>
</head>
<body>
{{title}} spends {{calc}}
</body>
</html>
And that's all! It's running and you can create your blog with it for example.
There's already a lot of Node.js frameworks that you can find on GitHub if you search for "node". But there's nothing really mature at this time.
Update (10 Feb 2010)
I forgot to mention that the mustache.js I've used was tweaked. We just have to add this following code at the end of the mustache.js file:
for (var name in Mustache)
if (Object.prototype.hasOwnProperty.call(Mustache, name))
exports[name] = Mustache[name];
This will be merge into the main library in the near future and you can find the whole file here