In the last post I showed you how to get started with Node.js on Windows. Easy, wasn’t it? Remarkably so since there was no install requirement. This time we are going to tweak the system a bit then learn how to return static files like plain ol’ Html, Css and Javascript files.
Note, for posterity's sake, that all of this is based on node.js as of version 0.4.7. If I had to bet money, I'd bet that things will change. So, dear future reader, this post may be out of date by the time you read this.
Making Things Easier
Last time we got started by unzipping the file that contained all the node files. That worked great but it would be nice to be able to have multiple node sites and not have to copy those files around. Let’s make that easy by copying those files to somewhere else (like “C:\Program Files\Node”) and adding a Path entry for it.
Computer > Right-click and hit properties > Advanced System Settings (on the left) > Environment Variables > System Variables > Path > add “C:\Program Files\Node”
So now the files that we use can be in a directory all their own and you still only have to type “node.exe server.js” to get started. Woot!
And another tip, keep Fiddler open while you mess around with node. It is very helpful to be able to see what was returned with each request. If you haven’t used Fiddler, you are missing out! Download it.
Serving Up A File
So starting a server and returning some Html hard-coded in Javascript is awesome like we did last time, but it would be even better if we could do this by serving up Html files. Let’s do that. Now, in IIS land we would expect to just be able to start the server and it would server up static files like css, js and html but that’s not the default behavior of node. Like I said in the last post, it’s raw, it’s low level and unless you use a pre-made node package (talk about that some other time) you get to do it all yourself. So let’s pick up ourselves by our own bootstraps and get this thing done.
First, let’s create a very basic html page. Maybe something like this:
<html>
<head>
<title>Rockin' Page</title>
</head>
<body>
<p>This is a page. For realz, yo.</p>
</body>
</html>
We’ll start by adding a reference to the “fs” (FileSystem) module. This is for file i/o. Below we’ll interogate the url passed in to decide which static file to return but for now we’ll just return the one created above. Line 8 shows how to read the file from disk:
var http = require('http');
var fs = require('fs');
http.createServer(function (request, response) {
console.log('request starting...');
fs.readFile('./index.htm', function(error, content) {
if (error) {
response.writeHead(500);
response.end();
}
else {
response.writeHead(200, { 'Content-Type': 'text/html' });
response.end(content, 'utf-8');
}
});
}).listen(8125);
console.log('Server running at http://127.0.0.1:8125/');
The first parameter I pass is the path to the file. The beginning “./” starts the path off at the root, then follows the file name. The second is the callback that fires when the file has finished reading from disk. The callback will return an error (if one happened) or the contents of the file. If there is an error, I’m returning a 500. Otherwise I’m returning the file. Sweet.
Serving Up Files
And this would be completely sufficient if we were serving up a single page website that had no includes…which is rare. So we should handle being able to return various static files. Let’s do this in two stages. First, let’s handle looking at the url to decide which file is being requested and return that. We will also import and use the path module to make sure a file exists before we try to read it. For grins, we’ll also add in a reference to a javascript (jquery) and css file and hook them up to see if they work.
var http = require('http');
var path = require('path');
var fs = require('fs');
http.createServer(function (request, response) {
console.log('request starting...');
var filePath = '.' + request.url;
if (filePath == './')
filePath = './index.htm';
path.exists(filePath, function(exists) {
if (exists) {
fs.readFile(filePath, function(error, content) {
if (error) {
response.writeHead(500);
response.end();
}
else {
response.writeHead(200, { 'Content-Type': 'text/html' });
response.end(content, 'utf-8');
}
});
}
else {
response.writeHead(404);
response.end();
}
});
}).listen(8125);
console.log('Server running at http://127.0.0.1:8125/');
<html>
<head>
<title>Rockin' Page</title>
<link type="text/css" rel="stylesheet" href="style.css" />
<script type="text/javascript" src="jquery-1_6.js"></script>
</head>
<body>
<p>This is a page. For realz, yo.</p>
</body>
<script type="text/javascript">
$(document).ready(function() {
alert('happenin');
});
</script>
</html>
Okay, so that’s cool and all but you might have noticed a problem: we are serving up .js and .css files as content type ‘text/html’. That’s just not cool. Let’s accommodate that by setting the content type by the extension of the file.
var http = require('http');
var fs = require('fs');
var path = require('path');
http.createServer(function (request, response) {
console.log('request starting...');
var filePath = '.' + request.url;
if (filePath == './')
filePath = './index.htm';
var extname = path.extname(filePath);
var contentType = 'text/html';
switch (extname) {
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
}
path.exists(filePath, function(exists) {
if (exists) {
fs.readFile(filePath, function(error, content) {
if (error) {
response.writeHead(500);
response.end();
}
else {
response.writeHead(200, { 'Content-Type': contentType });
response.end(content, 'utf-8');
}
});
}
else {
response.writeHead(404);
response.end();
}
});
}).listen(8125);
console.log('Server running at http://127.0.0.1:8125/');
And voila! Everything is being served up as it should.
What’s Next? Next we’ll talk about writing files. It’s easy. We’ll even receiving some json and using that for writing our files. Stay tuned!

Ido
Marak
I appreciate you blogging about Node.js, but this post is probably going to hurt more then it helps.
1. You should mention some of the many ( well developed ) static file modules already available
2. You shouldn't be doing a fs.readFile on every http request, this is a terrible design
3. You should be using the .pipe() api for static file serving
If you'd like more help / information I suggest you join the #Node.js room on irc.freenode.net
mikeal
dude, you gotta use streaming. you're bring the entire contents of the file in to memory and then pushing it out the network, you should really stream the file from disk to the socket.
also, you probably wanna check for the GET method. this is off the top of my head.
var basepath = '/files'
http.createServer(function (req, resp) {
if (req.method !== 'GET') {
req.writeHead(400);
req.end();
return;
}
var s = fs.createReadStream(path.join(basepath, req.path));
s.on('error', function () {
req.writeHead(404);
req.end();
})
s.once('fd', function () {req.writeHead(200);});
s.pipe(resp);
})
Eric Sowell
@Ido - Glad you found it useful.
@Marak - 1. Perhaps I shall. 2. Agree. Also not claiming that I'm creating the next big and best file server. 3. I'll look into that.
I'm experimenting, seeing what works and what doesn't. I'm too much of a Node noob to think this is more than that, and perhaps I need more disclaimer in that regard. I'm not against using other packages but at the moment I'm just interested in messing around with the core bits. And thanks for the tips, I may drop by and lurk on #Node.js.
@mikeal - I will look into that. Looks interesting and makes sense. Thanks.
Hay
I would recommend using something like Connect instead of the raw API. Makes serving static files a lot easier.
http://senchalabs.github.com/connect/
Eric
I will look into that one. I'm messing around with other things at the moment but will be circling back around to nodejs here pretty soon. Thanks for the tip.
Eli
Excellent article.
To the point and fast :D
I'm falling in love with Node.js
John
While I agree that in practice you would use a robust, existing library for this, rather than writing your own, this post was extremely useful to me as fellow Node.js newb who is interested in learning about the core Node offerings. When I someday use this in a real project, I will look at the Node ecosystem as a whole.
Thanks for a great introductory post, Eric!
Alec
Finally someone who managed to speak nongeek. Thanks be. I have my 'sandbox', such as it is, up and running.
Eduardo Costa
The Small Question of Doom(tm): what happens if someone sends you evil paths, like "../"? Example:
curl http://myserver:8125/../../../../etc/passwdborg
how about serving audio and video tag? any hints??