Accessing the HTTP message body (e.g. POST data) in node.js

A pretty common task for every web application is handling user input. While frameworks like express or connect provide convenient methods to access this data there seemed to be some confusion and the lack of a stripped down example for pure node.js HTTP server approaches. This post is intended to fill that gap, thoughts or an understandable but yet more minimalistic approach they can put in the API documentation are very welcome.

Full solutions

Update (12/07): This article has been getting a lot of link-love lately. For those of you who are looking for a full solution on handling and parsing forms in Node.js might be interested in:

The rest that follows, the original article, explains what those modules are doing under the hood.

Server and routes setup

For our example we need a HTTP server instance with a request handler that defines two routes:

  • ‘/’ A simple web page with a HTML form.
  • ‘/formhandler’ which accepts POST requests and handles the body that is generated by the form

All other requests (such as GET requests for favicon.ico) will be answered by a 404 Not found message.

To get started we use the following code which handles our two routes by sending back a 501 Not implemented response.

If you’re running the code locally you should now be able to visit http://localhost:8080/ in your browser.

A simple form

In the next step we change the browser for the start URL ‘/’ to present the user with a little web form by exchanging that part of the switch statement with a 200 OK response together with the appropriate HTML:

The ‘/formhandler’ part remains unchanged for now. If you run the script and visit the page in your browser you’ll be presented with a form asking for name and age.

Notice that the enctype parameter of the form is set to application/x-www-form-urlencoded. This specifies how the data is encoded and sent by the browser. URL encoding is okay for simple forms without file upload capabilities for example. What’s even better is that node.js provides a built-in module to parse this type of data. We’ll use it in a later step. The other parameters specify that by submitting the form the data should be sent to the ‘/formhandler’ route inside a POST request.

Read the message body

In a HTTP request we’re only looking for the header part that tells us what the browser is requesting. The body, that may contain everything from the two key/value combinations we use in the example up to multiple files, is seperated from the header part by two line breaks. What follows is mostly either URL encoded data or multipart/form-data – which is basically data sent in distinguishable small chunks.

In node.js the request event we’re already handling is emitted when the header is fully received and parsed. This is at a time where the client’s browser may still be sending body data, if we want to use it or not. The only route we set up that has to handle this data on our side is the ‘/formhandler’ route (and even then it should only use the data if it is inside a POST request). We exchange our previous 501 code for this route with the following code:

Multiple things are introduced here. First we check the HTTP headers if the request is a POST request (req.method). If that’s not the case (say because the user went to http://localhost:8080/formhandler without using the submit button) a simple¬†405 Method not supported error page is generated.

If the headers indicate that it’s really a POST request we start listening for two events of the request object:

  • data: Is triggered whenever body data comes in at the TCP socket level. Note that this doesn’t neessarily contain all the data, hence it’s called a chunk.
  • end: Is triggered when the HTTP request is completed. This indicates that all body data associated with it was read and the appropriate data events have been triggered.

Above code outputs every chunk of data it receives to the console (after converting it from a Buffer to String) and sends an empty 200 OK response to the client when the request is completely received.

Buffer and decode

So we learned that the body data of our HTTP request is encoded in someway, which means that we’ll have to decode it, as well as the fact that it may come in chunks of unknown size. The latter means that we will have to buffer each chunk of data until we have the data we need to decode it.

For the sake of simplicity we will do this by converting every chunk that comes in at a data event to a string variable and buffer that until the request is completed:

Note that the variable fullBody which contains all of the data is declared and initialized outside of the event handlers for data and end. This is essential because both need to have access to it.

Running this code will throw an error if you forgot to require the querystring module needed to decode the body data or the utils module which is just used to inspect() the decoded object. After adding those require() calls to the top of the code running the example will present the user with a JSON structure containing the username and userage keys and the data that has been entered into the form.

Okay that’s just weird, I want my $_POST

Granted, this blog post is intended to be really introductory and extensive for those who are just getting started with node.js.

If that’s inconvenient you should go with one of the previously mentioned frameworks that will do most of the work for you (e.g. the bodyDecoder middleware for connect).

What if I want file uploads & stuff?

If you have more complex or larger data structures in your HTTP body you might want to check the HTTP headers which encoding type is sent or process single key/value combinations as they get streamed in. If that’s the case and you are looking for a more convenient alternative you should check the body decoder middleware or module of one of the web server frameworks available in node.js or a standalone module that parses it.

For more details…

…check out the API docs at nodejs.org and join #Node.js on freenode or the mailing list if you get stuck. The full example code can be found in the gist.