Download as pdf or txt
Download as pdf or txt
You are on page 1of 102

Lee este tutorial en Espaol

Buy the ultimate Node.js bundle!


Introduction plus reference in one bundle 226 pages in total - PDF, ePub & MOBI Direct download - free updates

$7.99

$10.98

Buy now!

The Node Beginner Book


A Node.js tutorial by Manuel Kiessling

About
The aim of this document is to get you started with developing applications for Node.js, teaching you everything you need to know about "advanced" JavaScript along the way. It goes way beyond your typical "Hello World" tutorial.

Status
You are reading the final version of this book, i.e., updates are only done to correct errors or to reflect changes in new versions of Node.js. The code samples in this book are tested to work with Node.js version 0.4.9.

Intended audience
This document will probably fit best for readers that have a background similar to my own: experienced with at least one object-oriented language like Ruby, Python, PHP or Java, only little experience with JavaScript, and completely new

to Node.js. Aiming at developers that already have experience with other programming languages means that this document won't cover really basic stuff like data types, variables, control structures and the likes. You already need to know about these to understand this document. However, because functions and objects in JavaScript are different from their counterparts in most other languages, these will be explained in more detail.

Structure of this document


Upon finishing this document, you will have created a complete web application which allows the users of this application to view web pages and upload files. Which, of course, is not exactly world-changing, but we will go some extra miles and not only create the code that is "just enough" to make these use cases possible, but create a simple, yet complete framework to cleanly separate the different aspects of our application. You will see what I mean in a minute. We will start with looking at how JavaScript development in Node.js is different from JavaScript development in a browser. Next, we will stay with the good old tradition of writing a "Hello World" application, which is a most basic Node.js application that "does" something. Then, we will discuss what kind of "real" application we want to build, dissect the different parts which need to be implemented to assemble this application, and start working on each of these parts step-by-step. As promised, along the way we will learn about some of the more advanced concepts of JavaScript, how to make use of them, and look at why it makes sense to use these concepts instead of those we know from other programming languages. The source code of the finished application is available through the NodeBeginnerBook Github repository.

Table of contents
About Status Intended audience Structure of this document JavaScript and Node.js JavaScript and You A word of warning Server-side JavaScript "Hello World" A full blown web application with Node.js The use cases The application stack Building the application stack A basic HTTP server Analyzing our HTTP server Passing functions around How function passing makes our HTTP server work Event-driven callbacks How our server handles requests Finding a place for our server module What's needed to "route" requests? Execution in the kingdom of verbs Routing to real request handlers Making the request handlers respond How to not do it Blocking and non-blocking Responding request handlers with non-blocking operations Serving something useful Handling POST requests

Handling file uploads Conclusion and outlook

JavaScript and Node.js


JavaScript and You
Before we talk about all the technical stuff, let's take a moment and talk about you and your relationship with JavaScript. This chapter is here to allow you to estimate if reading this document any further makes sense for you. If you are like me, you started with HTML "development" long ago, by writing HTML documents. You came along this funny thing called JavaScript, but you only used it in a very basic way, adding interactivity to your web pages every now and then. What you really wanted was "the real thing", you wanted to know how to build complex web sites - you learned a programming language like PHP, Ruby, Java, and started writing "backend" code. Nevertheless, you kept an eye on JavaScript, you saw that with the introduction of jQuery, Prototype and the likes, things got more advanced in JavaScript land, and that this language really was about more than window.open(). However, this was all still frontend stuff, and although it was nice to have jQuery at your disposal whenever you felt like spicing up a web page, at the end of the day you were, at best, a JavaScript user, but not a JavaScript developer. And then came Node.js. JavaScript on the server, how cool is that? You decided that it's about time to check out the old, new JavaScript. But wait, writing Node.js applications is the one thing; understanding why they need to be written the way they are written means - understanding JavaScript. And this time for real. Here is the problem: Because JavaScript really lives two, maybe even three lives

Here is the problem: Because JavaScript really lives two, maybe even three lives (the funny little DHMTL helper from the mid-90's, the more serious frontend stuff like jQuery and the likes, and now server-side), it's not that easy to find information that helps you to learn JavaScript the "right" way, in order to write Node.js applications in a fashion that makes you feel you are not just using JavaScript, you are actually developing it. Because that's the catch: you already are an experienced developer, you don't want to learn a new technique by just hacking around and mis-using it; you want to be sure that you are approaching it from the right angle. There is, of course, excellent documentation out there. But documentation alone sometimes isn't enough. What is needed is guidance. My goal is to provide a guide for you.

A word of warning
There are some really excellent JavaScript people out there. I'm not one of them. I'm really just the guy I talked about in the previous paragraph. I know a thing or two about developing backend web applications, but I'm still new to "real" JavaScript and still new to Node.js. I learned some of the more advanced aspects of JavaScript just recently. I'm not experienced. Which is why this is no "from novice to expert" book. It's more like "from novice to advanced novice". If I don't fail, then this will be the kind of document I wish I had when starting with Node.js.

Server-side JavaScript
The first incarnations of JavaScript lived in browsers. But this is just the context. It defines what you can do with the language, but it doesn't say much about what the language itself can do. JavaScript is a "complete" language: you can use it in many contexts and achieve everything with it you can achieve with any other "complete" language.

Node.js really is just another context: it allows you to run JavaScript code in the backend, outside a browser. In order to execute the JavaScript you intend to run in the backend, it needs to be interpreted and, well, executed. This is what Node.js does, by making use of Google's V8 VM, the same runtime environment for JavaScript that Google Chrome uses. Plus, Node.js ships with a lot of useful modules, so you don't have to write everything from scratch, like for example something that outputs a string on the console. Thus, Node.js is really two things: a runtime environment and a library. In order to make use of these, you need to install Node.js. Instead of repeating the process here, I kindly ask you to visit the official installation instructions. Please come back once you are up and running.

"Hello World"
Ok, let's just jump in the cold water and write our first Node.js application: "Hello World". Open your favorite editor and create a file called helloworld.js. We want it to write "Hello World" to STDOUT, and here is the code needed to do that:

console.log("Hello World");

Save the file, and execute it through Node.js:

node helloworld.js

This should output Hello World on your terminal.

Ok, this stuff is boring, right? Let's write some real stuff.

Ok, this stuff is boring, right? Let's write some real stuff.

A full blown web application with Node.js


The use cases
Let's keep it simple, but realistic: The user should be able to use our application with a web browser The user should see a welcome page when requesting http://domain/start which displays a file upload form By choosing an image file to upload and submitting the form, this image should then be uploaded to http://domain/upload, where it is displayed once the upload is finished

Fair enough. Now, you could achieve this goal by googling and hacking together something. But that's not what we want to do here. Furthermore, we don't want to write only the most basic code to achieve the goal, however elegant and correct this code might be. We will intentionally add more abstraction than necessary in order to get a feeling for building more complex Node.js applications.

The application stack


Let's dissect our application. Which parts need to be implemented in order to fulfill the use cases? We want to serve web pages, therefore we need an HTTP server Our server will need to answer differently to requests, depending on which URL the request was asking for, thus we need some kind of router in order to map requests to request handlers To fullfill the requests that arrived at the server and have been routed using the router, we need actual request handlers The router probably should also treat any incoming POST data and give it to the request handlers in a convenient form, thus we need request data handling

We not only want to handle requests for URLs, we also want to display content when these URLs are requested, which means we need some kind of view logic the request handlers can use in order to send content to the user's browser Last but not least, the user will be able to upload images, so we are going to need some kind of upload handling which takes care of the details

Let's think a moment about how we would build this stack with PHP. It's not exactly a secret that the typical setup would be an Apache HTTP server with mod_php5 installed. Which in turn means that the whole "we need to be able to serve web pages and receive HTTP requests" stuff doesn't happen within PHP itself. Well, with node, things are a bit different. Because with Node.js, we not only implement our application, we also implement the whole HTTP server. In fact, our web application and its web server are basically the same. This might sound like a lot of work, but we will see in a moment that with Node.js, it's not. Let's just start at the beginning and implement the first part of our stack, the HTTP server.

Building the application stack


A basic HTTP server
When I arrived at the point where I wanted to start with my first "real" Node.js application, I wondered not only how to actually code it, but also how to organize my code. Do I need to have everything in one file? Most tutorials on the web that teach you how to write a basic HTTP server in Node.js have all the logic in one place. What if I want to make sure that my code stays readable the more stuff I implement? Turns out, it's relatively easy to keep the different concerns of your code separated, by putting them in modules.

This allows you to have a clean main file, which you execute with Node.js, and clean modules that can be used by the main file and among each other. So, let's create a main file which we use to start our application, and a module file where our HTTP server code lives. My impression is that it's more or less a standard to name your main file index.js. It makes sense to put our server module into a file named server.js. Let's start with the server module. Create the file server.js in the root directory of your project, and fill it with the following code:

var http = require("http"); http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); }).listen(8888);

That's it! You just wrote a working HTTP server. Let's prove it by running and testing it. First, execute your script with Node.js:

node server.js

Now, open your browser and point it at http://localhost:8888/. This should display a web page that says "Hello World". That's quite interesting, isn't it. How about talking about what's going on here and leaving the question of how to organize our project for later? I promise we'll get back to it.

Analyzing our HTTP server


Well, then, let's analyze what's actually going on here. The first line requires the http module that ships with Node.js and makes it

accessible through the variable http. We then call one of the functions the http module offers: createServer. This function returns an object, and this object has a method named listen, and takes a numeric value which indicates the port number our HTTP server is going to listen on. Please ignore for a second the function definition that follows the opening bracket of http.createServer. We could have written the code that starts our server and makes it listen at port 8888 like this:

var http = require("http"); var server = http.createServer(); server.listen(8888);

That would start an HTTP server listening at port 8888 and doing nothing else (not even answering any incoming requests). The really interesting (and, if your background is a more conservative language like PHP, odd looking) part is the function definition right there where you would expect the first parameter of the createServer() call. Turns out, this function definition IS the first (and only) parameter we are giving to the createServer() call. Because in JavaScript, functions can be passed around like any other value.

Passing functions around


You can, for example, do something like this:

function say(word) { console.log(word); } function execute(someFunction, value) { someFunction(value); }

execute(say, "Hello");

Read this carefully! What we are doing here is, we pass the function say as the first parameter to the execute function. Not the return value of say, but say itself! Thus, say becomes the local variable someFunction within execute, and execute can call the function in this variable by issuing someFunction() (adding brackets). Of course, because say takes one parameter, execute can pass such a parameter when calling someFunction. We can, as we just did, pass a function as a parameter to another function by its name. But we don't have to take this indirection of first defining, then passing it we can define and pass a function as a parameter to another function in-place:

function execute(someFunction, value) { someFunction(value); } execute(function(word){ console.log(word) }, "Hello");

We define the function we want to pass to execute right there at the place where execute expects its first parameter. This way, we don't even need to give the function a name, which is why this is called an anonymous function. This is a first glimpse at what I like to call "advanced" JavaScript, but let's take it step by step. For now, let's just accept that in JavaScript, we can pass a function as a parameter when calling another function. We can do this by assigning our function to a variable, which we then pass, or by defining the function to pass inplace.

How function passing makes our HTTP server work

With this knowledge, let's get back to our minimalistic HTTP server:

var http = require("http"); http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); }).listen(8888);

By now it should be clear what we are actually doing here: we pass the createServer function an anonymous function. We could achieve the same by refactoring our code to:

var http = require("http"); function onRequest(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888);

Maybe now is a good moment to ask: Why are we doing it that way?

Event-driven callbacks
The answer is a) not that easy to give (at least for me), and b) lies in the very nature of how Node.js works. It's event-driven, which is the reason why it's so fast. You might want to take the time to read Felix Geisendrfer's excellent post Understanding node.js for some background explanation. It all boils down to the fact that Node.js works event-driven. Oh and yes, I, too, don't know exactly what that means. But I will try and explain, why this makes sense for us who want to write web based applications in Node.js. When we call the http.createServer method, we of course not only want to have a

When we call the http.createServer method, we of course not only want to have a server listening at some port, we also want to do something when there is an HTTP request to this server. The problem is, this happens asynchronously: it happens at any given time, but we only have a single process in which our server runs. When writing PHP applications, we aren't bothered by this at all: whenever there is an incoming HTTP request, the webserver (usually Apache) forks a new process for just this request, and starts the according PHP script from scratch, which is then executed from top to bottom. So in regards of control flow, we are in the midst of our Node.js program when a new request arrives at port 8888 - how to handle this without going insane? Well, this is where the event-driven design of Node.js/JavaScript actually helps, although we need to learn some new concepts in order to master it. Let's see how these concepts are applied in our server code. We create the server, and pass a function to the method creating it. Whenever our server receives a request, the function we passed will be called. We don't know when this is going to happen, but we now have a place where we can handle an incoming request. It's our passed function, no matter if we first defined it or passed it anonymously. This concept is called a callback. We pass into some method a function, and the method uses this function to call back if an event related to the method occurs. At least for me, this took some time to understand. Just read Felix' blog post again if you are still unsure. Let's play around a bit with this new concept. Can we prove that our code continues after creating the server, even if no HTTP request happened and the callback function we passed isn't called? Let's try it:

var http = require("http"); function onRequest(request, response) { console.log("Request received.");

response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started.");

Note that I use console.log to output a text whenever the onRequest function (our callback) is triggered, and another text right after starting the HTTP server. When we start this (node server.js, as always), it will immediately output "Server has started." on the command line. Whenever we request our server (by opening http://localhost:8888/ in our browser), the message "Request received." is printed on the command line. Event-driven asynchronous server-side JavaScript with callbacks in action :-) (Note that our server will probably write "Request received." to STDOUT two times upon opening the page in a browser. That's because most browser will try to load the favicon by requesting http://localhost:8888/favicon.ico whenever you open http://localhost:8888/).

How our server handles requests


Ok, let's quickly analyze the rest of our server code, that is, the body of our callback function onRequest(). When the callback fires and our onRequest() function gets triggered, two parameters are passed into it: request and response. Those are objects, and you can use their methods to handle the details of the HTTP request that occured and to respond to the request (i.e., to actually send something over the wire back to the browser that requested your server). And our code does just that: Whenever a request is received, it uses the response.writeHead() function to send an HTTP status 200 and content-type in the HTTP response header, and the response.write() function to send the text "Hello World" in the HTTP response body.

At last, we call response.end() to actually finish our response. At this point, we don't care for the details of the request, which is why we don't use the request object at all.

Finding a place for our server module


Ok, I promised we will get back to how to organize our application. We have the code for a very basic HTTP server in the file server.js, and I mentioned that it's common to have a main file called index.js which is used to bootstrap and start our application by making use of the other modules of the application (like the HTTP server module that lives in server.js). Let's talk about how to make server.js a real Node.js module that can be used by our yet-to-be-written index.js main file. As you may have noticed, we already used modules in our code, like this:

var http = require("http"); ... http.createServer(...);

Somewhere within Node.js lives a module called "http", and we can make use of it in our own code by requiring it and assigning the result of the require to a local variable. This makes our local variable an object that carries all the public methods the http module provides. It's common practice to choose the name of the module for the name of the local variable, but we are free to choose whatever we like:

var foo = require("http"); ... foo.createServer(...);

Fine, it's clear how to make use of internal Node.js modules. How do we create our own modules, and how do we use them? Let's find out by turning our server.js script into a real module. Turns out, we don't have to change that much. Making some code a module means we need to export those parts of its functionality that we want to provide to scripts that require our module. For now, the functionality our HTTP server needs to export is simple: scripts requiring our server module simply need to start the server. To make this possible, we will put our server code into a function named start, and we will export this function:

var http = require("http"); function start() { function onRequest(request, response) { console.log("Request received."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

This way, we can now create our main file index.js, and start our HTTP there, although the code for the server is still in our server.js file. Create a file index.js with the following content:

var server = require("./server"); server.start();

As you can see, we can use our server module just like any internal module: by requiring its file and assigning it to a variable, its exported functions become

available to us. That's it. We can now start our app via our main script, and it still does exactly the same:

node index.js

Great, we now can put the different parts of our application into different files and wire them together by making them modules. We still have only the very first part of our application in place: we can receive HTTP requests. But we need to do something with them - depending on which URL the browser requested from our server, we need to react differently. For a very simple application, you could do this directly within the callback function onRequest(). But as I said, let's add a bit more abstraction in order to make our example application a bit more interesting. Making different HTTP requests point at different parts of our code is called "routing" - well, then, let's create a module called router.

What's needed to "route" requests?


We need to be able to feed the requested URL and possible additional GET and POST parameters into our router, and based on these the router then needs to be able to decide which code to execute (this "code to execute" is the third part of our application: a collection of request handlers that do the actual work when a request is received). So, we need to look into the HTTP requests and extract the requested URL as well as the GET/POST parameters from them. It could be argued if that should be part of the router or part of the server (or even a module of its own), but let's just agree on making it part of our HTTP server for now. All the information we need is available through the request object which is passed as the first parameter to our callback function onRequest(). But to

interpret this information, we need some additional Node.js modules, namely url and querystring. The url module provides methods which allow us to extract the different parts of a URL (like e.g. the requested path and query string), and querystring can in turn be used to parse the query string for request parameters:

url.parse(string).query | url.parse(string).pathname | | | | | ------ ------------------http://localhost:8888/start?foo=bar&hello=world ------| | | | querystring(string)["foo"] | | querystring(string)["hello"]

We can, of course, also use querystring to parse the body of a POST request for parameters, as we will see later. Let's now add to our onRequest() function the logic needed to find out which URL path the browser requested:

var http = require("http"); var url = require("url"); function start() { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

Fine. Our application can now distinguish requests based on the URL path requested - this allows us to map requests to our request handlers based on the

URL path using our (yet to be written) router. In the context of our application, it simply means that we will be able to have requests for the /start and /upload URLs handled by different parts of our code. We will see how everything fits together soon. Ok, it's time to actually write our router. Create a new file called router.js, with the following content:

function route(pathname) { console.log("About to route a request for " + pathname); } exports.route = route;

Of course, this code basically does nothing, but that's ok for now. Let's first see how to wire together this router with our server before putting more logic into the router. Our HTTP server needs to know about and make use of our router. We could hard-wire this dependency into the server, but because we learned the hard way from our experience with other programming languages, we are going to loosely couple server and router by injecting this dependency (you may want to read Martin Fowlers excellent post on Dependency Injection for background information). Let's first extend our server's start() function in order to enable us to pass the route function to be used by parameter:

var http = require("http"); var url = require("url"); function start(route) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(pathname); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); }

http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

And let's extend our index.js accordingly, that is, injecting the route function of our router into the server:

var server = require("./server"); var router = require("./router"); server.start(router.route);

Again, we are passing a function, which by now isn't any news for us. If we start our application now (node index.js, as always), and request an URL, you can now see from the application's output that our HTTP server makes use of our router and passes it the requested pathname:

bash$ node index.js Request for /foo received. About to route a request for /foo

(I omitted the rather annoying output for the /favicon.ico request).

Execution in the kingdom of verbs


May I once again stray away for a while and talk about functional programming again? Passing functions is not only a technical consideration. With regard to software design, it's almost philosophical. Just think about it: in our index file, we could have passed the router object into the server, and the server could have called this object's route function. This way, we would have passed a thing, and the server would have used this thing to do something. Hey, router thing, could you please route this for me?

But the server doesn't need the thing. It only needs to get something done, and to get something done, you don't need things at all, you need actions. You don't need nouns, you need verbs. Understanding the fundamental mind-shift that's at the core of this idea is what made me really understand functional programming. And I did understand it when reading Steve Yegge's masterpiece Execution in the Kingdom of Nouns. Go read it now, really. It's one of the best writings related to software I ever had the pleasure to encounter.

Routing to real request handlers


Back to business. Our HTTP server and our request router are now best friends and talk to each other as we intended. Of course, that's not enough. "Routing" means, we want to handle requests to different URLs differently. We would like to have the "business logic" for requests to /start handled in another function than requests to /upload. Right now, the routing "ends" in the router, and the router is not the place to actually "do" something with the requests, because that wouldn't scale well once our application becomes more complex. Let's call these functions, where requests are routed to, request handlers. And let's tackle those next, because unless we have these in place there isn't much sense in doing anything with the router for now. New application part, new module - no surprise here. Let's create a module called requestHandlers, add a placeholder function for every request handler, and export these as methods of the module:

function start() { console.log("Request handler 'start' was called."); } function upload() { console.log("Request handler 'upload' was called."); } exports.start = start;

exports.upload = upload;

This allows us to wire the request handlers into the router, giving our router something to route to. At this point we need to make a decision: do we hard-code usage of the requestHandlers module into the router, or do we want a bit more dependency injection? Although dependency injection, like every other pattern, shouldn't be used only for the sake of using it, in this case it makes sense to loosely couple the router and its request handlers, and thus making the router really reusable. This means we need to pass the request handlers from our server into our router, but this feels even more wrong, which is why we should go the whole way and pass them to the server from our main file, and passing it on to the router from there. How are we going to pass them? Right now we have two handlers, but in a real application, this number is going to increase and vary, and we sure don't want to fiddle around mapping requests to handlers in the router anytime a new URL / request handler is added. And having some if request == x then call handler y in the router would be more than ugly. A varying number of items, each mapped to a string (the requested URL)? Well, sounds like an associative array would be a perfect fit. Well, this finding is slightly disappointed by the fact that JavaScript doesn't provide associative array - or does it? Turns out, it's actually objects that we want to use if we need an associative array! There's a nice introduction to this at http://msdn.microsoft.com/enus/magazine/cc163419.aspx, let me quote the relevant part: In C++ or C#, when were talking about objects, we're referring to instances of classes or structs. Objects have different properties and methods, depending on which templates (that is, classes) they are instantiated from. That's not the case with JavaScript objects. In JavaScript, objects are just collections of name/value pairs - think of a

JavaScript object as a dictionary with string keys. If JavaScript objects are just collections of name/value pairs, how can they have methods? Well, the values can be strings, numbers etc. - or functions! Ok, now finally back to the code. We decided we want to pass the list of requestHandlers as an object, and in order to achieve loose coupling we want to inject this object into the route(). Let's start with putting the object together in our main file index.js:

var server = require("./server"); var router = require("./router"); var requestHandlers = require("./requestHandlers"); var handle = {} handle["/"] = requestHandlers.start; handle["/start"] = requestHandlers.start; handle["/upload"] = requestHandlers.upload; server.start(router.route, handle);

Although handle is more of a "thing" (a collection of request handlers), I propose we name it like a verb, because this will result in a fluent expression in our router, as we will see soon. As you can see, it's really simple to map different URLs to the same request handler: by adding a key/value pair of "/" and requestHandlers.start, we can express in a nice and clean way that not only requests to /start, but also requests to / shall be handled by the start handler. After defining our object, we pass it into the server as an additional parameter. Let's change our server.js to make use of it:

var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(handle, pathname);

response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

We've added the handle parameter to our start() function, and pass the handle object on to the route() callback, as its first parameter. Let's change the route() function accordingly, in our router.js file:

function route(handle, pathname) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](); } else { console.log("No request handler found for " + pathname); } } exports.route = route;

What we do here is, we check if a request handler for the given pathname exists, and if it does, we simply call the according function. Because we can access our request handler functions from our object just as we would access an element of an associative array, we have this nice fluent handle[pathname](); expression I talked about earlier: "Please, handle this pathname". Ok, that's all we need to wire server, router, and request handlers together! When starting our application and requesting http://localhost:8888/start in our browser, we can prove that the correct request handler was indeed called:

Server has started. Request for /start received. About to route a request for /start Request handler 'start' was called.

And opening http://localhost:8888/ in our browser proves that these requests,

And opening http://localhost:8888/ in our browser proves that these requests, too, are indeed handled by the start request handler:

Request for / received. About to route a request for / Request handler 'start' was called.

Making the request handlers respond


Beautiful. Now if only the request handlers could actually send something back to the browser, that would be even better, right? Remember, the "Hello World" your browser displays upon requesting a page still comes from the onRequest function in our server.js file. "Handling request" means "answering requests" after all, thus we need to enable our request handlers to speak with the browser just like our onRequest function does.

How to not do it
The straight-forward approach we - as developers with a background in PHP or Ruby - might want to follow is actually very deceitful: it works like a charm, seems to make a lot of sense, and then suddenly screws things up when we don't expect it. What I mean by "straight-forward approach" is this: make the request handlers return() the content they want to display to the user, and send this response data in the onRequest function back to the user. Let's just do this, and then see why it's not such an overly good idea. We start with the request handlers and make them return what we would like to display in the browser. We need to change requestHandlers.js to this:

function start() { console.log("Request handler 'start' was called.");

return "Hello Start"; } function upload() { console.log("Request handler 'upload' was called."); return "Hello Upload"; } exports.start = start; exports.upload = upload;

Good. Likewise, the router needs to return to the server what the request handlers return to him. We therefore need to edit router.js like this:

function route(handle, pathname) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { return handle[pathname](); } else { console.log("No request handler found for " + pathname); return "404 Not found"; } } exports.route = route;

As you can see, we also return some text if the request could not be routed. And last but not least, we need to refactor our server to make it respond to the browser with the content the request handlers returned via the router, transforming server.js into:

var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); response.writeHead(200, {"Content-Type": "text/plain"}); var content = route(handle, pathname) response.write(content); response.end(); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

If we start our rewritten application, everything works like a charm: requesting http://localhost:8888/start results in "Hello Start" being displayed in the browser, requesting http://localhost:8888/upload gives us "Hello Upload", and http://localhost:8888/foo produces "404 Not found". Ok, then why is that a problem? The short answer: because we will run into problems if one of the request handlers wants to make use of a non-blocking operation in the future. Let's take a bit more time for the long answer.

Blocking and non-blocking


As said, the problems will arise when we include non-blocking operations in the request handlers. But let's talk about blocking operations first, then about nonblocking operations. And instead of trying to explain what "blocking" and "non-blocking" means, let's demonstrate ourselves what happens if we add a blocking operation to our request handlers. To do this, we will modify our start request handler to make it wait 10 seconds before returning its "Hello Start" string. Because there is no such thing as sleep() in JavaScript, we will use a clever hack for that. Please modify requestHandlers.js as follows:

function start() { console.log("Request handler 'start' was called."); function sleep(milliSeconds) { var startTime = new Date().getTime(); while (new Date().getTime() < startTime + milliSeconds); } sleep(10000); return "Hello Start"; } function upload() { console.log("Request handler 'upload' was called.");

return "Hello Upload"; } exports.start = start; exports.upload = upload;

Just to make clear what that does: when the function start() is called, Node.js waits 10 seconds and only then returns "Hello Start". When calling upload(), it returns immediately, just like before. (Of course, you should imagine that instead of sleeping for 10 seconds, there would be a real life blocking operation in start(), like some sort of long-running computation.) Let's see what this change does. As always, we need to restart our server. This time, I ask you to follow a slightly complex "protocol" in order to see what happens: First, open two browser windows or tabs. In the first browser window, please enter http://localhost:8888/start into the address bar, but do not yet open this url! In the second browser window's address bar, enter

http://localhost:8888/upload, and again, please do not yet hit enter. Now, do as follows: hit enter on the first window ("/start"), then quickly change to the second window ("/upload") and hit enter, too. What you will notice is this: The /start URL takes 10 seconds to load, as we would expect. But the /upload URL also takes 10 seconds to load, although there is no sleep() in the according request handler! Why? Because start() contains a blocking operation. Like in "it's blocking everything else from working". And that is a problem, because, as the saying goes: "In node, everything runs in parallel, except your code". What that means is that Node.js can handle a lot of concurrent stuff, but doesn't do this by splitting everything into threads - in fact, Node.js is single-threaded.

Instead, it does so by running an event loop, and we the developers can make use of this - we should avoid blocking operations whenever possible, and use nonblocking operations instead. But to do so, we need to make use of callbacks by passing functions around to other functions that might do something that takes some time (like, e.g. sleep for 10 seconds, or query a database, or do some expensive calculation). This way we are saying "Hey, probablyExpensiveFunction(), please do your stuff, but I, the single Node.js thread, am not going to wait right here until you are finished, I will continue to execute the lines of code below you, so would you please take this callbackFunction() here and call it when you are finished doing your expensive stuff? Thanks!" (If you would like to read about that in more detail, please have a look at Mixu's post on Understanding the node.js event loop.) And we will now see why the way we constructed the "request handler response handling" in our application doesn't allow us to make proper use of non-blocking operations. Once again, let's try to experience the problem first-hand by modifying our application. We are going to use our start request handler for this again. Please modify it to reflect the following (file requestHandlers.js):

var exec = require("child_process").exec; function start() { console.log("Request handler 'start' was called."); var content = "empty"; exec("ls -lah", function (error, stdout, stderr) { content = stdout; }); return content; } function upload() { console.log("Request handler 'upload' was called."); return "Hello Upload"; }

exports.start = start; exports.upload = upload;

As you can see, we just introduced a new Node.js module, child_process. We did so because it allows us to make use of a very simple yet useful non-blocking operation: exec(). What exec() does is, it executes a shell command from within Node.js. In this example, we are going to use it to get a list of all files in the current directory ("ls lah"), allowing us to display this list in the browser of a user requesting the /start URL. What the code does is straightforward: create a new variable content (with an initial value of "empty"), execute "ls -lah", fill the variable with the result, and return it. As always, we will start our application, and visit http://localhost:8888/start. Which loads a beautiful web page that displays the string "empty". What's going wrong here? Well, as you may have already guessed, exec() does its magic in a non-blocking fashion. That's a good thing, because this way we can execute very expensive shell operations (like, e.g., copying huge files around or similar stuff) without forcing our application into a full stop as the blocking sleep operation did. (If you would like to prove this, replace "ls -lah" with a more expensive operation like "find /"). But we aren't exactly happy with our elegant non-blocking operation, when our browser doesn't display its result, right? Well, then, let's fix it. And while we are at it, let's try to understand why the current architecture doesn't work. The problem is that exec(), in order to work non-blocking, makes use of a callback function.

In our example, it's an anonymous function which is passed as the second parameter to the exec() function call:

function (error, stdout, stderr) { content = stdout; }

And herein lies the root of our problem: our own code is executed synchronous, which means that immediately after calling exec(), Node.js continues to execute return content;. At this point, content is still "empty", due to the fact that the callback function passed to exec() has not yet been called - because exec() operates asynchronous. Now, "ls -lah" is a very inexpensive and fast operation (unless there are millions of files in the directory). Which is why the callback is called relatively expeditious - but it nevertheless happens asynchronously. Thinking about a more expensive command makes this more obvious: "find /" takes about 1 minute on my machine, but if I replace "ls -lah" with "find /" in the request handler, I still immediately receive an HTTP response when opening the /start URL - it's clear that exec() does something in the background, while Node.js itself continues with the application, and we may assume that the callback function we passed into exec() will be called only when the "find /" command has finished running. But how can we achieve our goal, i.e. showing the user a list of files in the current directory? Well, after learning how to not do it, let's discuss how to make our request handlers respond to browser requests the right way.

Responding request handlers with non-blocking operations


I've just used the phrase "the right way". Dangerous stuff. Quite often, there is no single "right way". But one possible solution for this is, as often with Node.js, to pass functions

But one possible solution for this is, as often with Node.js, to pass functions around. Let's examine this. Right now, our application is able to transport the content (which the request handlers would like to display to the user) from the request handlers to the HTTP server by returning it up through the layers of the application (request handler -> router -> server). Our new approach is as follows: instead of bringing the content to the server, we will bring the server to the content. To be more precise, we will inject the response object (from our server's callback function onRequest()) through the router into the request handlers. The handlers will then be able to use this object's functions to respond to requests themselves. Enough explanation, here is the step by step recipe on how to change our application. Let's start with our server.js:

var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(handle, pathname, response); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

Instead of expecting a return value from the route() function, we pass it a third parameter, our response object. Furthermore, we removed any response method calls from the onRequest() handler, because we now expect route to take care of that. Next comes router.js:

function route(handle, pathname, response) {

function route(handle, pathname, response) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](response); } else { console.log("No request handler found for " + pathname); response.writeHead(404, {"Content-Type": "text/plain"}); response.write("404 Not found"); response.end(); } } exports.route = route;

Same pattern: instead of expecting a return value from our request handlers, we pass the respond object on. If no request handler can be used, we now take care of responding with a proper "404" header and body ourselves. And last but not least, we modify requestHandlers.js:

var exec = require("child_process").exec; function start(response) { console.log("Request handler 'start' was called."); exec("ls -lah", function (error, stdout, stderr) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write(stdout); response.end(); }); } function upload(response) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello Upload"); response.end(); } exports.start = start; exports.upload = upload;

Our handler functions need to accept the response parameter, and have to make use of them in order to respond to the request directly. The start handler will respond from within the anonymous exec() callback, and the upload handler still simply replies with "Hello Upload", but now by making

use of the response object. If we start our application again (node index.js), this should work as expected. If you would like to prove that an expensive operation behind /start will no longer block requests for /upload from answering immediately, then modify your requestHandlers.js as follows:

var exec = require("child_process").exec; function start(response) { console.log("Request handler 'start' was called."); exec("find /", { timeout: 10000, maxBuffer: 20000*1024 }, function (error, stdout, stderr) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write(stdout); response.end(); }); } function upload(response) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello Upload"); response.end(); } exports.start = start; exports.upload = upload;

This will make HTTP requests to http://localhost:8888/start take at least 10 seconds, but requests to http://localhost:8888/upload will be answered immediately, even if /start is still computing.

Serving something useful


Until now, what we have done is all fine and dandy, but we haven't created any value for the customers of our award-winning website. Our server, router, and request handlers are in place, thus now we can begin to add content to our site which allows our users to interact and walk through the use case of choosing a file, uploading this file, and viewing the uploaded file in the browser. For the sake of simplicity we will assume that only image files are going

to be uploaded and displayed through the application. Ok, let's take it step by step, but with most of the techniques and principles of JavaScript explained by now, let's at the same time accelerate a bit. This author likes to hear himself talking way too much anyways. Here, step by step means roughly two steps: We will first look at how to handle incoming POST requests (but not file uploads), and in a second step, we will make use of an external Node.js module for the file upload handling. I've chosen this approach for two reasons. First, handling basic POST requests is relatively simple with Node.js, but still teaches us enough to be worth exercising it. Second, handling file uploads (i.e., multipart POST requests) is not simple with Node.js, and therefore is beyond the scope of this tutorial, but using an external module is itself a lesson that makes sense to be included in a beginner's tutorial.

Handling POST requests


Let's keep this banally simple: We will present a textarea that can be filled by the user and submitted to the server in a POST request. Upon receiving and handling this request, we will display the content of the textarea.

The HTML for this textarea form needs to be served by our /start request handler, so let's add it right away, in file requestHandlers.js:

function start(response) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" content="text/html; '+ 'charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" method="post">'+ '<textarea name="text" rows="20" cols="60"></textarea>'+ '<input type="submit" value="Submit text" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end();

} function upload(response) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello Upload"); response.end(); } exports.start = start; exports.upload = upload;

Now if this isn't going to win the Webby Awards, then I don't know what could. You should see this very simple form when requesting http://localhost:8888/start in your browser. If not, you probably didn't restart the application. I hear you: having view content right in the request handler is ugly. However, I decided to not include that extra level of abstraction (i.e., separating view and controller logic) in this tutorial, because I think that it doesn't teach us anything worth knowing in the context of JavaScript or Node.js. Let's rather use the remaining screen space for a more interesting problem, that is, handling the POST request that will hit our /upload request handler when the user submits this form. Now that we are becoming expert novices, we are no longer surprised by the fact that handling POST data is done in a non-blocking fashion, by using asynchronous callbacks. Which makes sense, because POST requests can potentially be very large nothing stops the user from entering text that is multiple megabytes in size. Handling the whole bulk of data in one go would result in a blocking operation. To make the whole process non-blocking, Node.js serves our code the POST data in small chunks, callbacks that are called upon certain events. These events are data (an new chunk of POST data arrives) and end (all chunks have been received). We need to tell Node.js which functions to call back to when these events occur. This is done by adding listeners to the request object that is passed to our

onRequest callback whenever an HTTP request is received. This basically looks like this:

request.addListener("data", function(chunk) { // called when a new chunk of data was received }); request.addListener("end", function() { // called when all chunks of data have been received });

The question arises where to implement this logic. We currently can access the request object in our server only - we don't pass it on to the router and the request handlers, like we did with the response object. In my opinion, it's an HTTP servers job to give the application all the data from a requests it needs to do its job. Therefore, I suggest we handle the POST data processing right in the server and pass the final data on to the router and the request handlers, which then can decide what to do with it. Thus, the idea is to put the data and end event callbacks in the server, collecting all POST data chunks in the data callback, and calling the router upon receiving the end event, while passing the collected data chunks on to the router, which in turn passes it on to the request handlers. Here we go, starting with server.js:

var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var postData = ""; var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); request.setEncoding("utf8"); request.addListener("data", function(postDataChunk) { postData += postDataChunk; console.log("Received POST data chunk '"+ postDataChunk + "'."); }); request.addListener("end", function() {

route(handle, pathname, response, postData); }); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

We basically did three things here: First, we defined that we expect the encoding of the received data to be UTF-8, we added an event listener for the "data" event which step by step fills our new postData variable whenever a new chunk of POST data arrives, and we moved the call to our router into the end event callback to make sure it's only called when all POST data is gathered. We also pass the POST data into the router, because we are going to need it in our request handlers. Adding the console logging on every chunk that is received probably is a bad idea for production code (megabytes of POST data, remember?), but makes sense to see what happens. I suggest playing around with this a bit. Put small amounts of text into the textarea as well as lots of text, and you will see that for the larger texts, the data callback is indeed called multiple times. Let's add even more awesome to our app. On the /upload page, we will display the received content. To make this possible, we need to pass the postData on to the request handlers, in router.js:

function route(handle, pathname, response, postData) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](response, postData); } else { console.log("No request handler found for " + pathname); response.writeHead(404, {"Content-Type": "text/plain"}); response.write("404 Not found"); response.end(); } } exports.route = route;

And in requestHandlers.js, we include the data in our response of the upload request handler:

function start(response, postData) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" content="text/html; '+ 'charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" method="post">'+ '<textarea name="text" rows="20" cols="60"></textarea>'+ '<input type="submit" value="Submit text" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, postData) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("You've sent: " + postData); response.end(); } exports.start = start; exports.upload = upload;

That's it, we are now able to receive POST data and use it in our request handlers. One last thing for this topic: what we pass on to the router and the request handlers is the complete body of our POST request. We will probably want to consume the individual fields that make up the POST data, in this case, the value of the text field. We already read about the querystring module, which assists us with this:

var querystring = require("querystring"); function start(response, postData) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" content="text/html; '+

'charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" method="post">'+ '<textarea name="text" rows="20" cols="60"></textarea>'+ '<input type="submit" value="Submit text" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, postData) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("You've sent the text: "+ querystring.parse(postData).text); response.end(); } exports.start = start; exports.upload = upload;

Well, for a beginner's tutorial, that's all there is to say about handling POST data.

Handling file uploads


Let's tackle our final use case. Our plan was to allow users to upload an image file, and display the uploaded image in the browser. Back in the 90's this would have qualified as a business model for an IPO, today it must suffice to teach us two things: how to install external Node.js libraries, and how to make use of them in our own code. The external module we are going to use is node-formidable by Felix Geisendrfer. It nicely abstracts away all the nasty details of parsing incoming file data. At the end of the day, handling incoming files is "only" about handling POST data - but the devil really is in the details here, and using a ready-made solution makes a lot of sense in this case. In order to make use of Felix' code, the according Node.js module needs to be installed. Node.js ships with its own package manager, dubbed NPM. It allows us to install external Node.js modules in a very convenient fashion. Given a working Node.js installation, it boils down to issuing

npm install formidable

on our command line. If the following output ends with

npm info build Success: formidable@1.0.2 npm ok

then we are good to go. The formidable module is now available to our own code - all we need to do is requiring it just like one of the built-in modules we used earlier:

var formidable = require("formidable");

The metaphor formidable uses is that of a form being submitted via HTTP POST, making it parseable in Node.js. All we need to do is create a new IncomingForm, which is an abstraction of this submitted form, and which can then be used to parse the request object of our HTTP server for the fields and files that were submitted through this form. The example code from the node-formidable project page shows how the different parts play together:

var formidable = require('formidable'), http = require('http'), sys = require('sys'); http.createServer(function(req, res) { if (req.url == '/upload' && req.method.toLowerCase() == 'post') { // parse a file upload var form = new formidable.IncomingForm(); form.parse(req, function(err, fields, files) { res.writeHead(200, {'content-type': 'text/plain'}); res.write('received upload:\n\n'); res.end(sys.inspect({fields: fields, files: files})); }); return; } // show a file upload form res.writeHead(200, {'content-type': 'text/html'});

res.end( '<form action="/upload" enctype="multipart/form-data" '+ 'method="post">'+ '<input type="text" name="title"><br>'+ '<input type="file" name="upload" multiple="multiple"><br>'+ '<input type="submit" value="Upload">'+ '</form>' ); }).listen(8888);

If we put this code into a file and execute it through node, we are able to submit a simple form, including a file upload, and see how the files object, which is passed to the callback defined in the form.parse call, is structured:

received upload: { fields: { title: 'Hello World' }, files: { upload: { size: 1558, path: '/tmp/1c747974a27a6292743669e91f29350b', name: 'us-flag.png', type: 'image/png', lastModifiedDate: Tue, 21 Jun 2011 07:02:41 GMT, _writeStream: [Object], length: [Getter], filename: [Getter], mime: [Getter] } } }

In order to make our use case happen, what we need to do is to include the formparsing logic of formidable into our code structure, plus we will need to find out how to serve the content of the uploaded file (which is saved into the /tmp folder) to a requesting browser. Let's tackle the latter one first: if there is an image file on our local hardrive, how do we serve it to a requesting browser? We are obviously going to read the contents of this file into our Node.js server, and unsurprisingly, there is a module for that - it's called fs. Let's add another request handler for the URL /show, which will hardcodingly display the contents of the file /tmp/test.png. It of course makes a lot of sense to save a real png image file to this location first. We are going to modify requestHandlers.js as follows:

We are going to modify requestHandlers.js as follows:

var querystring = require("querystring"), fs = require("fs"); function start(response, postData) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" '+ 'content="text/html; charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" method="post">'+ '<textarea name="text" rows="20" cols="60"></textarea>'+ '<input type="submit" value="Submit text" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, postData) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("You've sent the text: "+ querystring.parse(postData).text); response.end(); } function show(response, postData) { console.log("Request handler 'show' was called."); fs.readFile("/tmp/test.png", "binary", function(error, file) { if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(error + "\n"); response.end(); } else { response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, "binary"); response.end(); } }); } exports.start = start; exports.upload = upload; exports.show = show;

We also need to map this new request handler to the URL /show in file index.js:

var server = require("./server"); var router = require("./router"); var requestHandlers = require("./requestHandlers");

var handle = {} handle["/"] = requestHandlers.start; handle["/start"] = requestHandlers.start; handle["/upload"] = requestHandlers.upload; handle["/show"] = requestHandlers.show; server.start(router.route, handle);

By restarting the server and opening http://localhost:8888/show in the browser, the image file saved at /tmp/test.png should be displayed. Fine. All we need to do now is add a file upload element to the form which is served at /start, integrate node-formidable into the upload request handler, in order to save the uploaded file to /tmp/test.png, embed the uploaded image into the HTML output of the /upload URL.

Step 1 is simple. We need to add an encoding type of multipart/form-data to our HTML form, remove the textarea, add a file upload input field, and change the submit button text to "Upload file". Let's do just that in file requestHandlers.js:

var querystring = require("querystring"), fs = require("fs"); function start(response, postData) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" '+ 'content="text/html; charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" enctype="multipart/form-data" '+ 'method="post">'+ '<input type="file" name="upload">'+ '<input type="submit" value="Upload file" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, postData) { console.log("Request handler 'upload' was called.");

response.writeHead(200, {"Content-Type": "text/plain"}); response.write("You've sent the text: "+ querystring.parse(postData).text); response.end(); } function show(response, postData) { console.log("Request handler 'show' was called."); fs.readFile("/tmp/test.png", "binary", function(error, file) { if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(error + "\n"); response.end(); } else { response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, "binary"); response.end(); } }); } exports.start = start; exports.upload = upload; exports.show = show;

Great. The next step is a bit more complex of course. The first problem is: we want to handle the file upload in our upload request handler, and there, we will need to pass the request object to the form.parse call of node-formidable. But all we have is the response object and the postData array. Sad panda. Looks like we will have to pass the request object all the way from the server to the router to the request handler. There may be more elegant solutions, but this approach should do the job for now. And while we are at it, let's remove the whole postData stuff in our server and request handlers - we won't need it for handling the file upload, and it even raises a problem: we already "consumed" the data events of the request object in the server, which means that form.parse, which also needs to consume those events, wouldn't receive any more data from them (because Node.js doesn't buffer any data). Let's start with server.js - we remove the postData handling and the request.setEncoding line (which is going to be handled by node-formidable itself), and we pass request to the router instead:

var http = require("http");

var http = require("http"); var url = require("url"); function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; console.log("Request for " + pathname + " received."); route(handle, pathname, response, request); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start;

Next comes router.js - we don't need to pass postData on anymore, and instead pass request:

function route(handle, pathname, response, request) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](response, request); } else { console.log("No request handler found for " + pathname); response.writeHead(404, {"Content-Type": "text/html"}); response.write("404 Not found"); response.end(); } } exports.route = route;

Now, the request object can be used in our upload request handler function. node-formidable will handle the details of saving the uploaded file to a local file within /tmp, but we need to make sure that this file is renamed to /tmp/test.png ourselves. Yes, we keep things really simple and assume that only PNG images will be uploaded. For now, fs.renameSync(path1, path2) will do the job. Beware! As the name implies, it works synchronous, thus if the rename operation should be expensive and take a long time, it will lead to blocking. Let's just agree that we are all grown-ups here and know what we are doing. Let's put the pieces of managing the uploaded file and renaming it together now, in file requestHandlers.js:

var querystring = require("querystring"), fs = require("fs"), formidable = require("formidable"); function start(response) { console.log("Request handler 'start' was called."); var body = '<html>'+ '<head>'+ '<meta http-equiv="Content-Type" content="text/html; '+ 'charset=UTF-8" />'+ '</head>'+ '<body>'+ '<form action="/upload" enctype="multipart/form-data" '+ 'method="post">'+ '<input type="file" name="upload" multiple="multiple">'+ '<input type="submit" value="Upload file" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, request) { console.log("Request handler 'upload' was called."); var form = new formidable.IncomingForm(); console.log("about to parse"); form.parse(request, function(error, fields, files) { console.log("parsing done"); fs.renameSync(files.upload.path, "/tmp/test.png"); response.writeHead(200, {"Content-Type": "text/html"}); response.write("received image:<br/>"); response.write("<img src='/show' />"); response.end(); }); } function show(response) { console.log("Request handler 'show' was called."); fs.readFile("/tmp/test.png", "binary", function(error, file) { if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(error + "\n"); response.end(); } else { response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, "binary"); response.end(); } }); } exports.start = start; exports.upload = upload; exports.show = show;

And that's it. Restart the server, and the complete use case will be available.

And that's it. Restart the server, and the complete use case will be available. Select a local PNG image from your hardrive, upload it to the server, and have it displayed in the web page.

Conclusion and outlook


Congratulations, our mission is accomplished! We wrote a simple yet full-fledged Node.js web application. We talked about server-side JavaScript, functional programming, blocking and non-blocking operations, callbacks, events, custom, internal and external modules, and a lot more. Of course, there's a lot of stuff we did not talk about: how to talk to a database, how to write unit tests, how to create external modules that are installable via NPM, or even something simple like how to handle GET requests. But that's the fate of every book aimed at beginners - it can't talk about every single aspect in every single detail. The good news is, the Node.js community is extremly vibrant (think of an ADHD kid on caffeine, but in a positive way), which means there are a lot of resources out there, and a lot of places to get your questions answered. The Node.js community wiki and the NodeCloud directory are probably the best starting points for more information.

This document was last updated on Sunday, July 10, 2011 at 3:52 AM EDT. The best way to stay informed about updates is to follow @ManuelKiessling on Twitter.

The Node Beginner Book is and will always be available for free. Furthermore, no money is needed to keep the project alive and progressing. But if you would like to support the author, you can do so using Flattr or PayPal:

25

Or, have a look at the eBook version (Kindle, ePub, PDF), which is available for $4.99 via Leanpub.

Like

and 28 others liked this.


Login

Add New Comment

Showing 216 comments

Sort by newest first

bcs

Excellent write up. But what do you think about this? http://www.usenix.org/events/h...
5 days ago
Shaan Batra

Like Reply

So have you written a more full book on all the various aspects of Node.JS? I am seriously willing to pay a good amount for it!
1 week ago
Manuel Kiessling

Like Reply

Hi Shaan, you might want to consider buying the Node.js bundle we are currently offering - it's The Node Beginner Book plus Hands-On Node.js by Pedro Teixeira. The latter one is definitely a "full" book covering all the ins and outs of Node.js. The bundle is for sale on LeanBundle.com, for $7.99: http://leanbundle.com/b/node
1 week ago in reply to Shaan Batra 1 Like
Shaan Batra

Like Reply

Perfect. Will go ahead and purchase that. Thanks!


1 week ago in reply to Manuel Kiessling
Affiliate Tom

Like Reply

I would also like to purchase one...


1 week ago in reply to Shaan Batra 1 Like
Manuel Kiessling

Like Reply

That's great! Just have a look at

http://leanpub.com/nodebeginne...
1 week ago in reply to Affiliate Tom
Sky Sports

Like Reply

Me too...
1 week ago in reply to Affiliate Tom
Dzmitry Krakadzeyau

Like Reply

I am a QA engineer and I first started to use JavaScript with TestComplete. As it was its M$ version -- JScript I studied MSDN (I am so sorry... I'll never do so, I promise). The documentation was really awful and I thought that the language is the same. Manuel, you changed my mind! Thank you very much for that.
3 weeks ago 1 Like
Erik Reppen

Like Reply

JScript is actually not that bad in all the ways that would count in Node.js. They've been ECMA compliant for a good decade now. It was the 10 years MS had been ignoring the W3C DOM API spec that was infuriating. I speak in the past tense because I think they're finally starting to shape up a bit.
3 weeks ago in reply to Dzmitry Krakadzeyau 1 Like
Bangladesh

Like Reply

I do agree with you mate...


1 week ago in reply to Erik Reppen
Dzmitry Krakadzeyau

Like Reply

Maybe, maybe... But their official documentation is repulsive. In addition TestComplete restricts JScript functionality so we needed to do tricks sometimes. I want to say that my first conclusion was incorrect. I could not simply get a good impression regarding JavaScript before. But this article did it.

3 weeks ago in reply to Erik Reppen 1 Like


Mike

Like Reply

I got all the way to the end and ran into a problem on the last step. I'm getting an error: "ENDENT, No such file or directory '/tmp/6f0320a3b21c779bb96af56abb843318'". The only line in my code that is referenced in the stack trace is the "fs.renameSync(files.upload.path, "tmp/test.png")" ... any ideas what I've done wrong??
3 weeks ago
Dzmitry Krakadzeyau

Like Reply

Mike, it seems you forgot a slash in "/tmp/test.png".


2 weeks ago in reply to Mike 1 Like
Zawaung

Like Reply

Thanks for the tutorial, best one so far on node.js! BTW, I think you can rewrite some parts, such as: For the handle object could use as object literal, var handle = { "/": requestHandlers.start //etc.. }; Also the section with the delayed response var exec = require("child_process").exec; function start(response) { console.log("Request handler 'start' was called."); exec("find /", { timeout: 10000, maxBuffer: 20000*1024 }, function (error, stdout, stderr) {

response.writeHead(200, {"Content-Type": "text/plain"}); response.write(stdout); response.end(); });} function upload(response) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello Upload"); response.end();} exports.start = start; exports.upload = upload; You could use setTimeout, setTimeout(function () { response.writeHead(200, {"Content-Type": "text/plain"}); response.write('yup'); response.end(); },10000); Then no need for child_process require.What you think?
3 weeks ago
Zawaung

Like Reply

just ignore the last part, not reading the page properly, Doh!
3 weeks ago in reply to Zawaung
Oscar Villarreal

Like Reply

Hi. So first of all thank you very much for investing the time and effort in doing this. I do have one question "var body = ''+ ''+ '<meta '+<br="" http-equiv="Content-Type"> 'content="text/html; charset=UTF-8" />'+ ''+ ''+ '<form action="/upload" method="post">'+ '<textarea cols="60" name="text" rows="20"></textarea>'+ '<input type="submit" value="Submit text">'+ ''+

''+ '';"Instead of doing this can we say use a template file in the back end where we "clone" or something of a kind that html and then send it? Thanks!</form>
4 weeks ago
Seluk Yavuz

Like Reply

Thank you. It was helpful to introduce to node.js.


1 month ago
baobaoyeye

Like Reply

It is vary perfect tutorial for me!!Thank you! I have a question : on the non-blocking part the result of "ls" can't show on the web page ,but it can be showed on console.log() Maybe I didn't understand.
1 month ago
theade

Like Reply

Hey, loved this tutorial, great style, great depth, very impressive, well done. For the sake of giving some constructive feedback (I know it's just an example) but I'm surprised that the handle object is in index. In playing about, I moved it to the router and called it routes, which makes router just about routing and keeps index clean (and fits with some php frameworks). Anyway, awesome job, thanks for your time, good luck with the book.
1 month ago
Prince Nishchal N E

Like Reply

Hi Manuel,

It's a very nice and elegant tutorial to Node Beginners. Thanks for posting the tutorial
1 month ago 1 Like
Tom Hauburger

Like Reply

Thanks for the absolutely wonderful introduction to node.js! While working through the tutorial, I had one question: 1. In our final editing of the request handlers, we reduced both the 'start' and 'show' handlers to only accept one function parameter (response), while the 'upload' handler still accepts two parameters (response, request). In the router code, however, we are blindly passing in two parameters to whichever handler gets called: handle[pathname](response,request); Is this a feature of JS that allows you to pass in the incorrect number of params? If so, it certainly makes our router code cleaner, but it also may cause some confusion. Since the code is working, I'm assuming that JS gracefully handles this by just using the first parameter in both the 'start' and 'show' cases. Any clarification here would be great! Thanks again for the wonderful work!
1 month ago 2 Likes
Prince Nishchal N E

Like Reply

JS allows handling params based on the function that is being called. In this case since the start and show have only one input parameter in their functions, the other parameter is omitted by the JS Engine. It is safe to pass any number of parameters in the function calls and JS handles this gracefully. However there will be some confusion to the developers initially who are new to JS way of

mapping parameters and function overloading.


1 month ago in reply to Tom Hauburger 1 Like
Manuel Kiessling

Like Reply

While the code is functional and working without problems, just as Prince Nushchal N E explained, it might be a good idea to add a note elaborating on function overloading to the book's text. I will do this as soon as I have the time.
1 month ago in reply to Prince Nishchal N E
Tom Hauburger

Like Reply

Awesome. Thanks for the response!


1 month ago in reply to Manuel Kiessling
brandontreb

Like Reply

Great tutorial! Thanks a ton.


1 month ago 1 Like
Paul Rogers

Like Reply

I'm a Happy Panda after reading this, :@ ) fantastic work and great tutor.
1 month ago 1 Like
LIor

Like Reply

Thanks alot!
1 month ago 1 Like
gradinariu bogdan

Like Reply

really nice! helped a lot


1 month ago 1 Like
Jab

Like Reply

Thank you ! You are my hero ! /Yossi.


1 month ago 1 Like
Jamey Cribbs

Like Reply

Bought the bundle yesterday. Read your book last night. I really like your writing style. And the example project you picked to build was perfect. I also thought the pace of the project progression (say that 3 times fast!) was spot-on. After seeing all the buzz about node.js, but never taking the time to understand what all the buzz was about, I feel like I now "get" why people are so excited. I would definitely buy any books you write in the future, so keep 'em coming!
1 month ago 1 Like
Manuel Kiessling

Like Reply

Hi Jamey, thanks for your encouraging answer! I'm happy to hear that the pace is okay - it's extremely difficult to know if the pace is ok if you are the author.

1 month ago in reply to Jamey Cribbs


pkazmier

Like Reply

Great tutorial Manuel! I like your writing style, the example, and your functional implementation. In the spirit of functional programming, I offer two other options that others may find useful:

(i) Change the implementation of 'route' to return the request handler function, then execute that function within 'onRequest'. The benefits of this approach is that the routing module performs only routing, which means the 'request' and 'response' objects don't have to be passed through to the router. Routing and request handling are then truly separate and allowing the possibility of alternate execution strategies. (ii) Change the implementation of 'route' module so that 'handle' doesn't have to be passed to the server only to be passed to the route function. I agree with staii's comment (far below) about the alternate approaches: (a) object-oriented approach of passing the route object instead of a route function, or (b) keeping with the functional approach, pass the handle array to the route module that creates a custom route function using a closure. After implementation both of these suggestions, the route module is used strictly for routing and not the execution of request handlers, while at the same time simplifying the route function as it would now only require a single argument: function route(pathname). Again, fantastic tutorial, well written, and nice to introduction to node.js, which I've been meaning to read about for some time now.
1 month ago 1 Like
Dan Kang

Like Reply

Hey Manuel, Great tutorial! The following lines appear multiple times and I believe "err" should be "error". if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(err + "\n"); Edit: Oops, seems like Mark Sost beat me to it!
1 month ago 1 Like Like Reply

Manuel Kiessling

Dan, Mark, thanks a million! I'll fix it asap.


1 month ago in reply to Dan Kang
Manuel Kiessling

Like Reply

Fixed with https://github.com/ManuelKiess... and updated the site. Thanks once again!
1 month ago in reply to Manuel Kiessling
Mark Sost

Like Reply

No problem, once again, great job!


1 month ago in reply to Manuel Kiessling
Mark Sost

Like Reply

Hey Manuel, not sure if someone else noted, but there's a typo in the show requestHandler function. response.write(err + "\n"); should actually be response.write(error, "\n"); within the if(error) case. Great tutorial, though! Learned a lot from it!
1 month ago 1 Like
Claudio Cicali

Like Reply

Hey Manuel *great work*! Two notes, though: it's not quite true that NPM ships with node. It has to be installed apart. And I'd have added some more thoughts about another important characteristic of Node that you glissed a little (maybe because it's quite "advanced stuff"). I mean the because Node.js doesn't buffer any data part. Thank you again
1 month ago 2 Likes
Manuel Kiessling

Like Reply

Hi Claudio, thanks for the input. I will improve these parts. Can you elaborate on the data buffering?
1 month ago in reply to Claudio Cicali
Claudio Cicali

Like Reply

The point is that the fact that node doesn't buffer anything makes it a nice tool for people trying to build something nasty with streams (like enconding/converting while uploading or downloading). Anyway, as I mentioned, it could be slighlty OT of the purpose of the book.
1 month ago in reply to Manuel Kiessling
bought the pdf

Like Reply

Is the complete source code available somewhere?


1 month ago 1 Like
Manuel Kiessling

Like Reply

Yes, it is, at https://github.com/ManuelKiess... I will put that link into the book, too!
1 month ago in reply to bought the pdf

Like Reply

Hey, guys read http://learnyousomeerlang.com/ first xD


1 month ago
Eric Danielson

Like Reply

Great tutorial! One small problem - this line in the last bit: response.write("You've sent the text: " + querystring.parse(postData)

["text"]); didn't work for me - had to be: response.write("You've sent the text: " + querystring.parse(postData).text); Otherwise, everything else was awesome - Thanks!
2 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Eric, I've fixed this with https://github.com/ManuelKiess... Thanks once again!


2 months ago in reply to Eric Danielson
Manuel Kiessling

Like Reply

Thanks for the hint! I'll look into it.


2 months ago in reply to Eric Danielson
Bob

Like Reply

Great tutorial! I have to agree with PING, though. :) Can you give us a ballpark of around when the rest will be posted? Tomorrow? Next week? Next month? Thanks.
2 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Bob, I've released the final version of the eBook on my publisher's site, http://leanpub.com/nodebeginne... It contains the final chapter on handling file uploads. The website will be updated soon, but until then, you can buy the eBook for only

$3.99 instead of $4.99 by using the coupon code "FirstAccessForCommentReaders" on Leanpub.


2 months ago in reply to Bob
Josue Gio

Like Reply

Coupon code expired or invalid


1 month ago in reply to Manuel Kiessling
Manuel Kiessling

Like Reply

That's true, it was only a limited offer. There is a bundle offer over at http://leanbundle.com/bundles/... - "The Node Beginner Book" plus "Hands-on Node.js" for only $7.99 instead of $10.98
1 month ago in reply to Josue Gio
Manuel Kiessling

Like Reply

It's awesome to see so much interested, thanks! I think next week should be feasible!
2 months ago in reply to Bob
PlNG

Like Reply

I don't want to be a pest, but please post more soon! I have plans to use node formidable, so I'm eagerly awaiting the next chapter.
2 months ago 1 Like
Manuel Kiessling

Like Reply

Hi PING, I've released the final version of the eBook on my publisher's site, http://leanpub.com/nodebeginne...

It contains the final chapter on handling file uploads. The website will be updated soon, but until then, you can buy the eBook for only $3.99 instead of $4.99 by using the coupon code "FirstAccessForCommentReaders" on Leanpub.
2 months ago in reply to PlNG
Drew Wells

Like Reply

Coupon code expired


1 month ago in reply to Manuel Kiessling
Manuel Kiessling

Like Reply

Yes, i'm afraid that was only a temporary offer.


1 month ago in reply to Drew Wells
Manuel Kiessling

Like Reply

The final version is close to being finished, promised!


2 months ago in reply to PlNG
Michael Rand

Like Reply

Awesome tutorial ... thanks much!


2 months ago 1 Like
Erik

Like Reply

Seriously the only tutorial that has helped me as a beginner to node.js. Great work! And please, please release more on more advanced topics if you can.
2 months ago 1 Like Like Reply

Vincent RABAH, DSI, blogueurPassionn de scalabilit,Green IT,virtualisation VMware,rsea

Great Job !!! I've written on my blog several articles (in French) about node.js : feel free to leave me comments :)

http://www.it-wars.com/?articl... http://www.it-wars.com/?articl... http://www.it-wars.com/?articl... http://www.it-wars.com/?articl... Regards !


2 months ago
seshness

Like Reply

This is brilliant. Also, have you publicized your bookmarks list? :)


2 months ago 2 Likes
Diego

Like Reply

Great stuff. Very well explained. Loved the linked articles too!
2 months ago 1 Like
Dev

Like Reply

Excellent!
2 months ago 1 Like
Vladimr ha

Like Reply

Thanks a lot for this useful and complex tutorial :) I'm a absolute beginner with node.js (and javascript as such), but I haven't found 1 thing. How could you do cross-domain requests from server-side? Lets say user want to see some photo, send request to node.js and I want to make GET request to some web service. I'm switching from java so maybe I'm just missing something.
2 months ago 1 Like
Craig

Like Reply

The http module can make get requests. Check the docs for more

info.
3 weeks ago in reply to Vladimr ha
Herman A. Junge

Like Reply

jQuery has some methods to enable cross-domain requests: http://api.jquery.com/jQuery.g...


2 months ago in reply to Vladimr ha 2 Likes
Patrick Mazzotta

Like Reply

Amazing. So helpful and your approach is very easy to read. Thanks so much! Just bought the book and am anxiously looking forward to the uploads and images section. SUGGESTION: maybe set up a system to allow donations for bounties for future works after you finish this book. For example, I'd donate to upvote adding a chapter on creating a template system for our router from this tutorial.
2 months ago 1 Like
Manuel Kiessling

Like Reply

I'll think about this, it's a good idea!


2 months ago in reply to Patrick Mazzotta
John Isaacks

Like Reply

Great tutorial, thanks, I would also like to see a tutorial on how to tie in mongoDB with node.js and have JS power virtually everything.
2 months ago 1 Like
Je_suis_un_sheepdog

Like Reply

I'd just be adding to what everyone else has said. Great tutorial Manuel.
3 months ago 1 Like Like Reply

Idophir

As I previously wrote, this is a great tutorial. Thanks! What I find missing is that the server is not fully operational as a web server. I've added a function that changes the content type of the response with regards to the file being returned: HTML, CSS, JS, etc. However I don't know how to have a default "handle" routing in index.js. What I would like to do is make the last handle route any un-handled requests to my web server function. This way, if I use my browser to get localhost/index.html, it will be handled correctly, including css and js. Any ideas?
3 months ago
Thor Erik

Like Reply

Great read, looking forward to the end :)


3 months ago 1 Like
Helge

Like Reply

Awesome tutorial! Finally understood the advantages and core concepts behind node.js and functional programming. Thanks a lot!
3 months ago 1 Like
S&S Network Solutions

Like Reply

Absolutely Excellent Tutorial!


3 months ago 1 Like
Pedro Henrique Fialho

Like Reply

Amazing, can't wait for the end! Keep the fun.


3 months ago 1 Like
crocodile

Like Reply

Great tutorial, just finished it...keep them coming


3 months ago 2 Likes
Brendan O'Connor

Like Reply

Thank you for the tutorial, I've been looking for a clear explanation of how to get started with node.js - I'm actually really very seriously considering moving my current project over to node.js from php after reading this and the links you've provided, it seems to be a better fit! Thanks again ^_^
3 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Brendan, I would love to hear about your experience should you really move your project to Node.js - and your reasons if you don't do it!
3 months ago in reply to Brendan O'Connor 1 Like
David J Wallace

Like Reply

Dude - you pretty much nailed it. I'm an elearning developer by trade, and in practice obviously an avid googler/stackoverflower/etc/etc, this tutorial intro to Node.js is not only a tribute to Node.js, but also a very fine piece of tech learning in general. Well done, thank you, and best wishes in completing whatever you feel you need to complete. - danjah / novice-come-advanced-novice
3 months ago 1 Like
Manuel Kiessling

Like Reply

It's absolutely awesome to get this kind of feedback - thank you very much! I hope to write the final chapter real soon.

3 months ago in reply to David J Wallace


Chris Cummings

Like Reply

Outstanding! Thanks for taking the time to write this.


3 months ago 1 Like
Manuel Kiessling

Like Reply

Hi all, just wanted to let you know that the book is now at 90%. I've added chapters on how to handle POST requests. As you will notice, I've decided to sell an eBook version via Leanpub.
3 months ago
Yudhisthira Attry

Like Reply

I am not getting response body for POST and PUT request. both request having body. code is given below var data = ""; req.setEncoding("utf8"); req.addListener("data", function(postDataChunk) { data += postDataChunk; AndroidLog.ReqStart("Contact POST = ", "Inside req.on data"); }); req.addListener("end", function() { AndroidLog.ReqStart("Contact POST = ", "Inside req.on end"); res.writeHead(404, {"Content-Type": "text/html"}); res.write("404 Not found"); res.end(); }); response status is always 200 ok and no response body. I am using mozila REST client.
1 month ago in reply to Manuel Kiessling Like Reply

Eugene Geronimo

Nicely done! Will be looking forward to your updates.


3 months ago 1 Like
Idophir

Like Reply

A small improvement: In the Routing to real request handlers chapter, you explain about the requestHandlers but do not instruct to start a new file (requestHandlers.js) for them.
3 months ago 2 Likes
Manuel Kiessling

Like Reply

Thanks mate, I'll have a look at it once I'm home. I'm currently on vacation, until next week.
3 months ago in reply to Idophir
Oscarvillarreal14

Like Reply

I have no words to express my thanks. I am a newb to back end coding but been working with javascript for a while. This definitely opens up a world of knowledge. If you ever write a book I'll buy it
3 months ago 2 Likes
Minikomi

Like Reply

Hi Manuel! This is really great. It's exactly what I had started doing on my own, by breaking apart express, but laid out in a much more logical manner. One thing - I thought this line read a little clunkily: ... but because we learned the hard way from our experience with other programming languages, we are going to loosely couple server and router by *injection* this dependency ...

perhaps should be ... but because we learned the hard way from our experience with other programming languages, we are going to loosely couple server and router by *injecting *this dependency ... Eagerly awaiting the forthcoming parts!
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi, Thanks for reading NodeBeginner. Andrew Gruner sent me a pull request today, fixing exactly this issue: https://github.com/ManuelKiess... Please check if ctrl-reload shows you the updated version of the page.
4 months ago in reply to Minikomi
Arian van Putten

Like Reply

I just realized, the whole requestHandler thing is a bit redundant... This is what I use in my code at the moment, it's alot cleaner: var handle = require('./handlers'); --- handlers.js--exports['/start'] = function (query,callback) { callback({responseCode:200}, 'Start'); }; // equivalent to exports['notfound'] but it's not a path (there is no location called notfound,) so there is no forward-slash, so I can use the dot notation. (this function is called when a pathname is requested that's not

in the handle object exports.notfound = function(query,callback) { } exports['/upload'] = function (query,callback) { callback({responseCode:200}, 'Not Implemented'); }; (Edited by a moderator)
4 months ago 3 Likes
Manuel Kiessling

Like Reply

Hi Arian, I've edited your comment, because the h1 HTML tags were interpreted by Disqus and displayed as real h1 headlines on the page, which broke the layout. I hope that's ok. Regarding your code, that makes a lot of sense, I didn't know one could use "exports" like this. I will see how I can integrate that into the book.
4 months ago in reply to Arian van Putten 1 Like
Arian van Putten

Like Reply

well, everything follows the JSON (Java Script Object Notation), even the exports object. the dot notation (object.property) is just syntax sugar for object['property']
4 months ago in reply to Manuel Kiessling
Manuel Kiessling

Like Reply

Hi everyone, just wanted to let you know that it could take a while for me to find the time and write the missing 20% of the book - worst case would be 2 weeks from now. Problem is I currently have too many other stuff that needs to get done.

Thanks for your support and your patience.


4 months ago
Arian van Putten

Like Reply

It might be nice if you could make a list on the repository that says what still has to be done, then maybe people can fork the project, work on those points on the list and then you can look at the forked projects if you see anything you might want to use in the final book.
4 months ago in reply to Manuel Kiessling 1 Like
Jak Spalding

Like Reply

The favicon requests got annoying, you can easily ignore them though: function start(route, handle) { function onRequest(request, response) { var pathname = url.parse(request.url).pathname; if (pathname != "/favicon.ico") { /* ignore all favicon.ico requests */ console.log("Request for "+pathname+" received."); route(handle, pathname, response); } /* end if */ } http.createServer(onRequest).listen(8888); console.log("Server has started"); }
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Jak, thanks for the hint. I hesitated adding more complexity to the code examples than absolutely necessary, but I understand that the additional requests showing up can be annoying. I will see if I include your code in the examples.
4 months ago in reply to Jak Spalding Like Reply

blcArmadillo

This is a wonderful guide. I've found it to be quite informative. Keep up the great work!
4 months ago 1 Like
Travis Person

Like Reply

This is probably one of the most amazing introductory guide to node.js that I have read yet! Amazing work, can't wait for the rest of the guide. This definitely gives me a starting point to jump into node. When I finished reading the section "JavaScript and You" I knew this was going to be an amazing guide. Your description fit me perfectly, really amazing work.
4 months ago 3 Likes
Guest

Like Reply

you can use curl and there will be no /favicon requests.


4 months ago 2 Likes
Neiliss

Like Reply

Excellent, hope that you will continue with this.


4 months ago 1 Like
Rob Brazier

Like Reply

Excellent tutorial. Keep up the great work!


4 months ago 2 Likes
Idophir

Like Reply

Your are the greatest! I finally found the time to install ubuntu on a spare laptop, added node and npm and started going through your well written book. I'm having fun

and I love how you make complicated things seem understandable. I'm about half way through the book and had a question. What do you feel about using frameworks such as "express" to handle most parts of the requests handling?
4 months ago 1 Like
Manuel Kiessling

Like Reply

My very subjective opinion: I think using frameworks makes a lot of sense! Building everything up from zero for every project simply is too tedious. You have to put a lot of effort into infrastructure work instead of putting this effort into what makes your project valuable to your users. But building everything up from zero makes a lot of sense if you want to learn a new technology. If you skip understanding the basis of a new technology and start using the frameworks built on this basis right away, then everything you learn from there on will be shallow. It works ok and you might even be relatively productive, but you will run into problems once you have to finish a project under time pressure, or if you need to find a solution for a problem the frameworks doesn't address. That, on the other hand, doesn't mean you need to know and understand every single line of Express before using it. But understanding the basic idea of these frameworks and their mechanics makes a lot of sense. I hope this tutorial can help you with this.
4 months ago in reply to Idophir 1 Like
sofia cardita

Like Reply

great great tutorial :) noticed a typo here "Our handler functions need to

accept the response parameter, and have to make use of them in order to repsond to the request directly." - notice the "repsond" Thanks :) I was needing some input on how to better organize code in node and this was just the thing to fit the bill!
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hey, I really love http://onesandzeros.posterous...., especially because I want to do something similar!


4 months ago in reply to sofia cardita
Manuel Kiessling

Like Reply

Hi Sofia, Thank you very much, I fixed the typo!


4 months ago in reply to sofia cardita
sofia cardita

Like Reply

great great tutorial :)


4 months ago 1 Like
Dominic Butler

Like Reply

Thanks a million for this article. I worked through it this morning and my only concern was that you might lose interest and not complete it. But I come back this evening and there is a huge update! Really excellent stuff and pitched at just the right level for me. Many thanks indeed.
4 months ago 1 Like
Manuel Kiessling

Like Reply

Thanks! Rest assured I'm having the fun of my life and am not going to loose interest anytime soon. One question: Is it difficult to pick up the new content if you come

back after an update happened? Especially if like today chapters that were already written are partly rewritten? Does the document need a clickable table of contents?
4 months ago in reply to Dominic Butler
Adrian Pomilio

Like Reply

It probably should have a clickable table of contents. It would be a nice to have.


4 months ago in reply to Manuel Kiessling
Manuel Kiessling

Like Reply

Hi Adrian, the site now has a table of contents - I hope you like it.
4 months ago in reply to Adrian Pomilio
Nikhil Bafna

Like Reply

Wow. This is simply the best Getting Started with node.js for me. Thanks!
4 months ago 1 Like
Savil Srivastava

Like Reply

Good Sir, this was absolutely awesome! I was looking to better understand node.js. Came across your article and was completely drawn in. Keep up the good work! Also, might I suggest leaving the Work In Progress notification up forever with whatever your next mini-goal is? It would serve to whet the reader's appetite further.
4 months ago 1 Like
Manuel Kiessling

Like Reply

It probably WILL be a neverending work in progress, simply because Node.js itself is.

4 months ago in reply to Savil Srivastava


Seth Etter

Like Reply

Fantastic. Best beginner node tutorial I've come across yet. Thanks for taking the time to explain in-depth the theory behind the workings of node.js, instead of just telling me what stuff to type. :)
4 months ago 1 Like
nketter

Like Reply

I'm new to Web development, having recently learned HTML, CSS, Javascript and jQuery. I was feeling ready to build my first "real" web app but didn't know if I should learn another language/framework (PHP or Ruby) and if so which one. I discovered your Book and am really excited about using node.js instead. Your book has been hugely instructive. Thanks so much! I hesitated to leave a comment as I see you reply to nearly all comments and frankly I'd rather you spend your time working on the next part of the "Book" than thanking me for my comment. So hopefully you will. :-)
4 months ago 1 Like
Manuel Kiessling

Like Reply

Haha, that's one cool comment! I promise I won't respond and instead work... D'oh! :-)
4 months ago in reply to nketter 1 Like
Vishal

Like Reply

Superb to read!!
4 months ago 1 Like
Jsnovice

Like Reply

This is an amazing tutorial. It manages to explain advanced (for a JS novice like me) concepts in a very accessible way. Keep it up! And

please continue working on this :)


4 months ago 1 Like
Usubov

Like Reply

Greate work. Thanks!


4 months ago 1 Like
hq

Like Reply

Like the way you tell the STORY. I will be back and read it.
4 months ago 1 Like
Ron Rieneckert

Like Reply

Just wanted to throw my thanks onto the pile. Cheers!


4 months ago 1 Like
Tarun

Like Reply

This is very helpful! Great job. In router.js line 3 "if (typeof handle[pathname] !== 'function')" should have equal to rather than not equal to.
4 months ago
Manuel Kiessling

Like Reply

My bad, thanks for finding it! I have fixed it and updated the page. (just for the record: if you ever need to urgently fix your site, but your notebook is in the room where your guests are sleeping, FTPOnTheGo for iPhone can help you out - an extremely well done app)
4 months ago in reply to Tarun Like Reply

Manuel Kiessling

Now also fixed in the repository: https://github.com/ManuelKiess...


4 months ago in reply to Manuel Kiessling
Tarun

Like Reply

Thanks! That was blazing fast!


4 months ago in reply to Manuel Kiessling
Jack Dempsey, Developer, Entrepreneur, builder of things

Like Reply

Manuel, First, thanks for putting this together! Really enjoy the flow and the brief trips into the world of Javascript. Very helpful. I'm currently learning CoffeeScript and really enjoying it. As such, I thought I'd follow along with your examples, but write them in CoffeeScript instead. I've created a git repo over at https://github.com/jackdempsey... Hope that's alright with you. If you haven't done any CoffeeScript, you should check it out! Look forward to the next chapters.
4 months ago
Manuel Kiessling

Like Reply

That's brilliant! I will definitely check it out as soon as possible (right now it's 11pm here and my wife demands I get some sleep...) I love CoffeeScript, but decided to keep the tutorial plain JS in order to not introduce too much at once. I already planned to point the reader at CoffeeScript at the end of the tutorial, and I will definitely refer to your project.

4 months ago in reply to Jack Dempsey


Jack Dempsey, Developer, Entrepreneur, builder of things

Like Reply

Yeah, that makes a lot of sense. I'll try and clean the project up and make sure it looks good at the end.
4 months ago in reply to Manuel Kiessling 1 Like
Manuel Kiessling

Like Reply

Jack, finally had a chance to take a look. Simply loving it! When the "main" book is finished I will add an "where to go from here" section, and will introduce CoffeeScript there. I will refer to your examples, and will also refer to them in a yet-to-be-added "download the sample code" section. Thanks for investing your time!
4 months ago in reply to Jack Dempsey Like Reply

Jack Dempsey, Developer, Entrepreneur, builder of things

Great, happy to help. Do you think it's worth tagging each stage of building the code so people can easily step through things? I often just jump to the end result and read over it instead.
4 months ago in reply to Manuel Kiessling
Manuel Kiessling

Like Reply

Me too. I have seen people putting a lot of effort into creating repos this way, but I don't believe it is used that often by readers,

and thus not really worth it.


4 months ago in reply to Jack Dempsey
staii

Like Reply

Good job! Just a small comment, passing your "handle object" to the server is not a clean abstraction. If I was doing this, I would either create an interface on the router to "register" handlers and their corresponding URLs, then pass the router itself to the server (rather than passing the route function). Either that, or I would define a createRouter() function that takes the "handle object", and returns a router function, saving the object in a closure.
4 months ago
Manuel Kiessling

Like Reply

You are absolutely right. But the challenge is - at least I feel it is to find the right balance regarding the example application. Complex and interesting enough to be a valuable source for learning and understanding, but not too complex and therefore hindering the reader from learning the stuff relevant for the tutorial by introducing stuff that is not relevant for the tutorial. Which, of course, leads to the discussion about what should be considered relevant and what not, and I'm far from being sure about that. However, my gut feeling is that the current level of abstraction in the application fits the purpose of learning Node.js quite well. In the tutorial I could point out that more or different layers of abstraction could make sense - but that's true for every part of the app, which means I would need to point it out again and again, and that might be annoying.

The good thing is, is that your excellent observation is now available in the comments, adding huge value to the tutorial. If you're fine with that I suggest we keep it that way. What's your opinion on this?
4 months ago in reply to staii 1 Like
staii

Like Reply

I see your point about finding a balance. Being the author, I think you have vision to tell what should go in, and what could add too much complexity. My comment was only meant as a suggestion. I guess having my suggestion in the comments is fair enough :)
4 months ago in reply to Manuel Kiessling 1 Like
Joel Plane

Like Reply

start() is only blocking because you included a blocking sleep() call. Without the sleep() call, it is non-blocking. However, "return"ing rather than using callbacks does prevent you from using non-blocking IO APIs, which I guess is your point.
4 months ago
Manuel Kiessling

Like Reply

Hi Joel, You're absolutely right. I simplified too much here and I'm going to rewrite that part the very way you explained it. Thanks for helping me with this one!
4 months ago in reply to Joel Plane
Joel Plane

Like Reply

Forgot to say thanks for a great read. I really enjoyed it.


4 months ago in reply to Manuel Kiessling
Alex Zirbel

Like Reply

Augh, I was extremely disappointed to hit the end - I was on a roll! It really speaks for this tutorial that I still feel like doing more node.js after getting this far, and I'm looking forward to the next chapters! So many nice things to be said about this tutorial, thanks so much for sharing it!
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Alex, sorry for the "cliffhanger" :-) I should probably point out at the very beginning that this is work in progress. However, really glad to hear you like it!
4 months ago in reply to Alex Zirbel
Jak Spalding

Like Reply

As long as this doesn't turn into "Lost" I'll be very happy! The guide's great, although I foolishly took time out of writing my dissertation to have a go :-)
4 months ago in reply to Manuel Kiessling
Incaandmaya

Like Reply

Excellent work....
4 months ago 1 Like
Diego

Like Reply

This article is awesome. Thanks and i'm waiting for the next chapters. Great job.
4 months ago 2 Likes
Adrian Pomilio

Like Reply

Thank you, you are a freaking rock star for doing this. I am up and running and enjoying all the info!
4 months ago 1 Like
Amul Patel

Like Reply

thanks for putting this together!! keep it up. > finish.


4 months ago 1 Like
whozman

Like Reply

You might want to consider having the article become "How to write your own express.js". That way one can grow their knowledge of node based on first principles and yet understand what express is all about.
4 months ago
Erskine

Like Reply

This is one of the best tutorials I've read. As a former Java coder, I've always found JavaScript to be a black art, but you have really simplified things with this tutorial. I'm looking forward to subsequent installments. Well done!
4 months ago 2 Likes
Manuel Kiessling

Like Reply

Hi Erskine, thank you so much for taking the time and reading NodeBeginner. Can't even describe how much fun the whole project is thanks to comments like yours!

4 months ago in reply to Erskine


Pat Farrell

Like Reply

Very nice work here. I've had trouble getting good info on code organization with Node, so this is exactly what I'm looking for. Other comments have mentioned AJAX, which I'm sure you have in mind. Are you going to handle communicating with some persistence layer? I'd find that helpful.
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Pat, I'm currently planning to describe the example application back-tofront, with using only barebone node. With this, the main part of the "book" would then be finished. However, building on that and - as someone called it - "branching out", i.e. explaining stuff like socket.io, DB handling, AJAX etc. makes a lot of sense, and I'll definitely consider this.
4 months ago in reply to Pat Farrell
Brett_Freeman

Like Reply

I was looking for a something to kickstart my learning of node.js and you've provided a great start. I'm looking forward to purchasing the final copy. I also wanted to add that by putting up this early draft for review (a la beta software) you're crowdsourcing both the grammar and technical review, not to mention created a huge pre-publication buzz. I don't recall seeing that in tech book previously and it's truly genius, surpassed only by how well you receive the comments and subsequently commit the edits.

I'm looking forward to watching the project progress toward.completion. Excellent work so far and thanks for including us in the process!
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Brett, can't thank you enough for the friendly review. To be honest, calling the whole project The Node Beginner *Book* was a bit of a stretch - I want to provide a thorough and complete introduction, but I'm not sure if the final product will deserve the name "book". Which is why, at this point, I'm not planning to release the final product as a real book (be it hardcopy or eBook) - it's just a fun project of mine, and I would like to keep it free under the creative commons. After all, I can assure you that comments like yours are a lot more rewarding than money.
4 months ago in reply to Brett_Freeman 1 Like
Brett_Freeman

Like Reply

If you're not going to publish I'd like to recommend a tip jar. I'm sure there are many others like me who would gladly kick in $5 - $10 in support of this fine effort.
4 months ago in reply to Manuel Kiessling 3 Likes Like Reply

Alan Hecht, Software Developer living in Atlanta, GA

+1 on the idea of a tip jar. I'd definitely kick in US $5 $10 as well.


4 months ago in reply to Brett_Freeman 2 Likes Like Reply

Manuel Kiessling

Brett, Alan, you guys are awesome, what can I say? I hope the project isn't compromised in any way by it, but I added a Flattr button (right above the comments). Check it out and tell me what you think.
4 months ago in reply to Alan Hecht 2 Likes Like Reply

Alan Hecht, Software Developer living in Atlanta, GA

Looks good. I don't think it would compromise what you're doing since it's entirely voluntary and pretty innocuous
4 months ago in reply to Manuel Kiessling 1 Like Like Reply

Henry Allen-Tilford

^_^ <3
4 months ago 1 Like
Dananjaya

Like Reply

I just finished reading the tutorial and can't wait until you put some more material. I haven't had the faintest idea about node.js before and I really like the build-up process which eventually leads to a full blown web app. Nice work and keep it up! Cheers!
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Dananjaya,

I'm glad you like it, that really keeps me going. If you find errors or think that parts of the text are not as good as you think they could be, please let me know!
4 months ago in reply to Dananjaya 1 Like
Matthew Case

Like Reply

I too am making the transition from "novice" to "advanced novice" and I was thinking about writing some blog posts on it, but I am far more pleased with what you're doing here. I'd gladly trade notes with you, especially once you get into more advanced topics. I'm currently using node, express and mongoose to make a beta signup page. I'm on the final step of dumping the email address into a MongoDB database via mongoose but struggling mightily with it. I definitely would suggest covering all of the basics in just node, and then branching out into the budding framework ecosystem such as express and talking about how these core concepts are implemented there. For example, Express has a really strong routing engine but it's definitely worth it to see how that is implemented from the ground up before having a framework handle it for you.
4 months ago 3 Likes
Manuel Kiessling

Like Reply

Hi Matthew, I have to admit that I haven't worked with Express and Mongoose yet. Once the basic introduction on how to build a full blown app with barebone node is finished, it makes a lot of sense to talk about Frameworks and databases, so yes, I would really love to hear your ideas and share notes. You can fork the document on Github at any time and send me your changes as pull requests: https://github.com/ManuelKiess... Of course you can also simply drop me a line in the comments or at manuel@kiessling.net Looking forward to it!

4 months ago in reply to Matthew Case 1 Like


Justin Herrick

Like Reply

This is such a great guide. I can't wait to finish reading it. Keep up the great work.
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Justin, I'm really happy you like it. I intend to add new chapters every day, I hope it works out!
4 months ago in reply to Justin Herrick
Darko Bunic

Like Reply

Excellent nodejs begginer tutorial ... Router part is done on the most elegant way! Do you have plan to explain socket.io too?
4 months ago 1 Like
Manuel Kiessling

Like Reply

I haven't played around with socket.io yet, thus my answer is "maybe". Of course, socket.io (arguably) isn't "beginner", which is why it probably wouldn't fit here.
4 months ago in reply to Darko Bunic
Ronny Bubke

Like Reply

Nice tutorial. node.js is really a quick way to realize small web projects. Unfortunately like every dynamic typed language not usable for big business projects cause of the lack of supporting refactoring and compile time checking like static languages. http://beust.com/weblog/2006/1...
4 months ago Like Reply

Manuel Kiessling

With "not usable" you mean not usable like for example the dynamic typed language PHP for the small web project Facebook?
4 months ago in reply to Ronny Bubke 1 Like
Ronny Bubke

Like Reply

Yes not usable maybe was a little bit to hard defined. I try to explain it so: the bigger the project will be the higher are the efforts for managing the software cause you have no compile time checks. This means you have to test 100% of the program code you write. Refactoring also is a huge problem cause you don't have static types. This means you replace all names with same name but that's can be the wrong behaviour. Facebook has a huge amount of developers which will suffer under this fact. To explain Facebooks choice of PHP is simple. It was not initially intended to be the "biggest" project in the world. Also it seems to be Mark Zuckerbergs favorite language at this time. It would be an interesting question what Facebook would do if they had the same language decission again. And what you maybe not know is that Facebook uses PHP just for their frontend. Have a look at this article: http://www.makeuseof.com/tag/f...
4 months ago in reply to Manuel Kiessling
probablyrobots

Like Reply

Great tutorial Manuel! I'm really looking forward to the next installment. typo "We we do here is" -> "What we do here is"
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi probablyrobots,

thanks for the info. I fixed this with commit https://github.com/ManuelKiess... and updated the site.
4 months ago in reply to probablyrobots
NodeJS learner

Like Reply

Finally I have found clear and great tutorial on nodeJS. Waiting for more! Thanks.
4 months ago 1 Like
shiirish

Like Reply

I think you and I went through the same growing pains while learning event drive JavaScript and node.js! It's almost like watching a time lapse of what I learned. And I love the conversational tone; following the content was actually enjoyable! Keep up the good work!
4 months ago 1 Like
Manuel Kiessling

Like Reply

I would love to learn from your experience, anything you would approach differently?
4 months ago in reply to shiirish
Joel Van Horn

Like Reply

Very well written and easy to follow. Really like your writing style. Many thanks!
4 months ago 3 Likes
Stephen Bush

Like Reply

Thanks for this! It's a solid start. I'll be following along.


4 months ago 1 Like
dtuite

Like Reply

Really nice tutorial. Looking forward to the rest of it! Thanks.


4 months ago 1 Like
Jamie Murai

Like Reply

I'm no REST expert by any means, but I think your interpretation of REST might be a little off. I don't think 'start' and 'upload' would be interpreted as resources. The thing you upload would more likely be a resource, in this case 'images'. Also, I believe it's the router's job to take a path, and then route to the resource, rather than taking a resource to begin with. So the route method should be route(path), rather than route(resource). It kinda follows from my first point, because the router might not always be routing to a resource, since not everything is a resource. I know the result is the same, but it would be more semantically accurate.
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Jamie, I'm not happy with the part talking about REST, and I'm going to modify it. Regarding the route(path) stuff, you are right, and I changed it accordingly, I hope you like it.
4 months ago in reply to Jamie Murai
Hello71

Like Reply

In "Finding a place for our server module", you define a global function first then export it. What you really should do is assign an anonymous function to it. Remember that closures can be used anywhere, not just as function parameters.
4 months ago
Hello71

Like Reply

Typo: "Requested received." -> ""Request received."


4 months ago
Manuel Kiessling

Like Reply

Thanks for hinting at this. I fixed it with commit https://github.com/ManuelKiess... and updated the page.
4 months ago in reply to Hello71
suvash

Like Reply

Always wanted to get started with Node, but didn't have enough time for it ! So glad that you came up with this post, will definitely check back as you add more ! Loads of Thanks !
4 months ago 1 Like
Jimmy Miller

Like Reply

This guidance is awesome. I am very impressed by the approachability and ease with which it can be read and digested.
4 months ago 1 Like
Zachary Hodge

Like Reply

This is awesome! I've been sifting through so many articles and blogs about node that have assumed waay too much for what I'm currently capable of. It's nice to see someone assume zero prior knowledge of node. Also, do you think you will be working with Express? If you plan on doing so, I'm glad you started with barebones node, because for me at least it was a lot easier to understand Express after working with node. Thanks for the article! Looking forward to more.
4 months ago Like Reply

Manuel Kiessling

That's exactly what I thought, keeping it barebone node at least in the beginning, in order to allow beginners to make the first step before the second. Maybe I'll expand it and add later chapters which explain how to do it in Express.
4 months ago in reply to Zachary Hodge
Zachary Hodge

Like Reply

Haha, cool. Either way, thanks for the resource!


4 months ago in reply to Manuel Kiessling 1 Like
Andy Jarrett

Like Reply

awesome and helpful! cheers for this resource


4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Andy, great to hear you like it. If you think there is anything that could be improved, just let me know!
4 months ago in reply to Andy Jarrett
Madeleine Alomar

Like Reply

Very nice and clear. Thank you! :)

4 months ago 1 Like

Like Reply

Manuel Kiessling

Hi Madeleine, glad to hear you think it's clear. I'm afraid it maybe still is a bit too chatty here and there, so if you have ideas for improvements, just let me know!
4 months ago in reply to Madeleine Alomar
Yuval Adam

Like Reply

Wonderful piece - lays out everything from the ground up. Great job!
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Yuval, good to hear that - this "everything from the ground up" approach is really important for me. Did you have to "leave" the site in order to search for information that was missing here (except for the external links of course)?
4 months ago in reply to Yuval Adam
Pharmapharma144

Like Reply

its wonderful! thank you


4 months ago 1 Like
Ben

Like Reply

Thank you very much for spending the time to put this together, I will definitely check back soon!
4 months ago 2 Likes
Manuel Kiessling

Like Reply

Hi Ben,

thanks for your friendly comment. I'm trying to update the document at least once per day.
4 months ago in reply to Ben
timonv

Like Reply

Nice article! Keep it coming.


4 months ago 1 Like
jimisir

Like Reply

this is really good. I'll be back for more definitely


4 months ago 1 Like
Sam Bolgert

Like Reply

Thank you for doing this. So far its already a great resource.
4 months ago 1 Like
Manuel Kiessling

Like Reply

Hi Sam, glad to hear you like it!


4 months ago in reply to Sam Bolgert
Prakhar

Like Reply

Thank you so much for this. I'm really grateful to you for all the pains you took for writing this.
4 months ago 1 Like
Manuel Kiessling

Like Reply

No pain at all - really loving it, especially when receiving such nice feedback like yours! Thank you so much!
4 months ago in reply to Prakhar Like Reply

Paul Gibler

This is one of the few beginner articles I made it all the way through because of how well it's written. These concepts are hardly new to me but you've laid it out in a way that showing other people what node.js is all about will be easy.
4 months ago 12 Likes
maxjaderberg

Like Reply

Same for me! Can't wait for the next parts...


4 months ago in reply to Paul Gibler
Manuel Kiessling

Like Reply

Hi Paul, can't express how much your comment made my day! I hope the full work will keep up to that.
4 months ago in reply to Paul Gibler 5 Likes
Tyler Jewell

Like Reply

Great tone and thorough explanations. It's a solid tutorial. I think it'll be important to expand the tutorial to show AJAX interactions with the client side along with some server-side DB access.
4 months ago 3 Likes
Manuel Kiessling

Like Reply

Hi Tyler, thanks for the suggestions. It's hard to decide what to include and what to not include in order to give beginners a focused experience, but on the other hand, it makes a lot of sense to simply build on the basic tutorial and introduce other concepts like those you mentioned on top.

4 months ago in reply to Tyler Jewell


Herman

Like Reply

Thank you so much for this. Please get this done as soon as possible!
4 months ago 2 Likes
Manuel Kiessling

Like Reply

Hi Herman, thanks for this. I promise I'll do my best!


4 months ago in reply to Herman 1 Like
theMaSch

Like Reply

please please get this done. Its great. I'd like to show this to people who are asking what node.js is good for. Thanks for this great work.
4 months ago 5 Likes
Manuel Kiessling

Like Reply

Hi theMaSch, thanks so much for taking the time to read and commenting the way you did. Writing NodeBeginner already was a lot of fun, but I think now I actually start loving the work!
4 months ago in reply to theMaSch
mikealrogers

Like Reply

you need to replace sys.puts with console.log, sys.puts might go away some day, it's technically a legacy API. also, i don't know if this complicates your tutorial too much or not, but if a module only exposes one object you can just replace exports with that object so that the return value of require is just that object.

var start = function (val) {...} module.exports = start //// require('test')('asdfa')


4 months ago 4 Likes
Manuel Kiessling

Like Reply

Hi Mikeal, thanks for the input, that's a really important point! I changed the usage of sys.puts to console.log throughout the whole document with commit https://github.com/ManuelKiess... The page already reflects this change. Regarding one-function-modules: I will definitely incorporate that!
4 months ago in reply to mikealrogers
Jann Horn

Like Reply

"Thus, node.js is really two things: a runtime environment and a library." Well, actually, node is the library, but V8 (that fast JS VM made by Google) is the VM that the code runs inside. But it's really a good tutorial!
4 months ago 3 Likes
Manuel Kiessling

Like Reply

Hi Jann, thanks for pointing this out. I rewrote that part to make things clearer (see commit https://github.com/ManuelKiess... ).

The page already reflects this change - do you think it makes more sense now?
4 months ago in reply to Jann Horn 1 Like
ama

Like Reply

@Manuel, I just wanted to thank you millions dude, it's super clear and to the point tutorial. Simply put, an amazing manual. SOMEONE PLEASE HELP: I'm a complete newbie to node.js and quite excited to try out the tutorial as I keep reading but unfortunately the codes don't seem to run; could you spare a second and perhaps see what I'm doing wrong: I downloaded the complete Windows based package from: http://node-js.prcn.co.cc http://node-js.prcn.co.cc/bin/... - extracted the folders - renamed folder: node-0.4.7-i686-pc-cygwin-complete to node-js - placed the folder in c: so my node-js folder is at: c:\node-js - run the node.exe file at: c:\node-js\bin\node.exe - opened notepad, saved the code for helloworld with the filename helloworld.js - placed the helloworld.js at c:\node-js\bin so the file is at c:\node-js\bin\helloworld.js - in the command line window open (the node.exe window) typed either of the following: > node helloworld.js OR > node c:\node-js\bin\helloworld.js but in both cases I only get (...) and nothing happens and no output from system happens. I have no idea what i'm doing wrong, I love to continue with

learning but this is stopping me from going further because I can't run any of the codes in the tutorial, any guidance would be so much appreciated, cheers, am.
2 months ago in reply to Manuel Kiessling
Adam Gruer

Like Reply

Try node helloworld.js from a cmd.exe window. e.g. c:\node-js\bin> node helloworld.js
2 months ago in reply to ama
Jann Horn

Like Reply

Yes, thank you.


4 months ago in reply to Manuel Kiessling Like Reply

M Subscribe by email S RSS

Reactions

Show more reactions Trackback URL


http://disqus.com/forums/nodebeginner/the_node_beginner_book/trackback/

The Node Beginner Book by Manuel Kiessling is licensed under a

Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. Permissions beyond the scope of this license may be available at manuel@kiessling.net.

You might also like