Web
Web is a new node module designed for building web applications. It's intended to replace the built-in http
module as well as middle-ware systems like connect and stack.
As the creator of the connect/stack interface, I've learned a lot over the years about what I could have done better. Also the built-in http
module in node itself has gotten bloated and complex since we've learned as we've gone on. Features can be added a lot easier than they can be removed. As a result, there are many different optional interfaces in the node HTTP interface.
The new interface for web app layers found in web
is closer modeled after the ideas in wsgi, rack, jsgi, strata.js and friends. This allows for easy module composition and a simple but powerful interface through which layers can work together to build a complex web application.
The Old Interface
In the old node interface as exposed by the http
module, you create an HTTP server and pass it an HTTP request handler function.
Usage looks like:
function handler(request, response) {
}
var server = http.createServer(handler);
server.listen(8080);
This interface alone is not easy to wrap application layers and so middle-ware dispatchers like connect and stack were invented to ease the pain. They still have several problems however.
Considering it's history, this module has a pretty good API, but there is only so much you can do if you're learning as you go and are unable to break backwards compatibility. Cruft will accumulate even under the watchful eye of the best maintainers.
The New Way
The new interface is changed slightly.
Firstly, instead of a response
object that's both response stream and object full of random methods, there is a respond
function that accepts status code, headers, and body.
Second, it's not bound to the net
module anymore. I believe that parsing HTTP and listening on a real network socket should be two different responsibilities. If you separate the two, then it's much easier to create mock servers that listen on fake sockets or exotic real servers that aren't TCP based.
var app = function (request, respond) {
}
var handler = require('web').socketHandler(app);
var server = require('net').createServer(handler);
server.listen(8080);
Built-in Middleware System
Unlike the http
module, web
makes it trivial to stack app layers. There is no need for a library because all that's needed is a simple function. Any function that implements the (request, respond)
interface is a valid web application.
Basic Logger Layer
Suppose you wanted to add a layer that logged all request and the response code the app gave them.
This can be done simple as:
function logger(app) {
return function(req, res) {
app(req, function (code, headers, body) {
console.log(req.method + " " + req.url.path + " " + code);
res(code, headers, body);
});
};
}
app = logger(app);
As you can see, there are places to do logic at several steps in a request and server lifetime.
Request Object
The request object contains the following properties.
req.method
This is the HTTP method in the request. It can have values like "GET", "POST", "PUT", "DELETE", and any other value the node http parser supports.
This is a hash of the headers with keys lowercases for easy access.
It can be used as:
if (req.headers["content-type"] === "application/json") { ... }
req.url
This is the result of node's url.parse on the http request url.
req.url.path
contains the original raw string if desired.
req.url.pathname
is the path alone without query strings.
req.versionMajor, req.versionMinor
These two properties tell you the version of the http request. They are usually either (1, 0) or (1, 1) for HTTP 1.0 and HTTP 1.1.
req.shouldKeepAlive
This is a hint set by node's http parser to tell the middleware if it should
keepalive the connection.
req.upgrade
This is another hint set by node's http parser. It's true for upgrade request like websocket connections.
req.body
This property is set by the web library. It's a readable node stream for the request body.
This is a raw array of alternating key/value pairs. For example, the headers from a curl request look like:
req.rawHeaders = [
'User-Agent', 'curl/7.26.0',
'Host', 'localhost:8080',
'Accept', '*/*' ]
Post response processing.
The built-in response function does a bit of post-processing after your app is
done to help your app be a proper http server. These post-processing filters can be configured in the socketHandler function's options argument.
options.autoDate = true
This options adds a Date
header with the current date as required by the HTTP spec if your response does not have a Date
header.
options.autoServer = "node.js " + process.version
Adds a Server
header with the running version of node if you don't have a Server
header. Set to some falsy value to disable or set to a new string to replace.
options.autoContentLength = true
If your body is a known size (not streaming) and you leave out the Content-Length
header, this will add one for you.
options.autoChunked = true
If you leave out a Content-Length
header and your body is streaming, it will replace your stream with a chunked encoded stream and set the proper Transfer-Encoding: chunked
header for you.
options.autoConnection = true
This will try to detect what the Connection
header should be. Usually either close
or keep-alive
. Also it will update req.shouldKeepAlive
which controls the tcp level connection behavior.