Off late I have seen many people struggling with creating a static Node server, also there have been questions like “How to have static & Dynamic files served by Node”. So here I have a fully functional server that can serve both static as well as dynamic files. Off course there is no need as there are many frameworks that can take care of these things. But always good to do things on your own to learn a new language or as we say Reinventing the wheels.
Below is the server that decides what content to serve based on URL. I have used /static/ to mark static content from URL pattern. That is any URL having this pattern would be considered static and would be served from file system.
var http = require('http'), url = require('url'), staticHandler = require('./staticHandler'); function start(router, handlers) { http.createServer(function(req, res) { var _url = url.parse(req.url).pathname; if(_url.indexOf('/static/') != -1) { staticHandler.handleStatic(_url, res); return; } else { var content = router(handlers, _url, req); res.writeHead(200, {"Content-Type": "text/html"}); res.write(content); res.end(); } }).listen(8888); console.log("Server started !! "); } exports.start = start;
Most of the code here is self explanatory, in case you are missing something I recommend you to read this post.
We now would be creating a static handler to pipe the file to the output stream.
var path = require('path'), fs = require('fs'); function handleStatic(pageUrl, response) { var filename = path.join(process.cwd(), pageUrl); path.exists(filename, function(exists) { if(!exists) { console.log("not exists: " + filename); response.writeHead(404, {'Content-Type': 'text/html'}); response.write('404 Not Found\n'); response.end(); return; } //Do not send Content type, browser will pick it up. response.writeHead(200); var fileStream = fs.createReadStream(filename); fileStream.on('end', function() { response.end(); }); fileStream.pipe(response); return; }); } exports.handleStatic = handleStatic;
fs.createReadStream would create a stream for the file. Instead of reading the entire file into memory and serving to the client, streaming is always better for performance.
In case you are creating your own version using the above code, do not forget to include response.end and a return statement.
For the dynamic part, I have also sent request object to the methods, this would enable them to read request parameters and relevant details to your dynamic code and thus help in generating dynamic data. You can return anything from the functions to your index.js page. It can be JSON as well, I am currently pushing HTML data back to the client.
var url = require("url"); function fetch(req) { console.log("Request handler 'fetch' was called."); return read(req); } function save(req) { console.log("Request handler 'Save' was called."); return read(req); } exports.fetch = fetch; exports.save = save; function read(req) { var content = ""; var _url = url.parse(req.url, true); var pathname = _url.pathname; console.log("Request for " + pathname + " received."); if(_url.query) { content += " Ohh! You have also provided me below data -</pre> <ul> <ul>";</ul> </ul> <ul> <ul>for(i in _url.query)</ul> </ul> <ul> <ul>{</ul> </ul> <ul> <ul>content += " <li>" + i + " = " + _url.query[i] +"</li> </ul> </ul> <ul> <ul>";</ul> </ul> <ul> <ul>}</ul> </ul> <ul>content += "</ul> <pre> "; } return content; }
This would enable server to serve request by save and fetch methods. Now last but not least, our index.js file or the server loader.
var server = require('./server'); var controller = require("./controller"); var urlResponseHandlers = require("./urlResponseHandlers"); var handle = {}; handle["/"] = urlResponseHandlers.fetch; handle["/fetch"] = urlResponseHandlers.fetch; handle["/save"] = urlResponseHandlers.save; server.start(controller.dispatch, handle);
Above we are simply mapping URL patterns to our functions we had defined above. Thus request for /fetch would be served by our fetch method while, save would server /save URL.
Now we just need a controller to map the URL patterns to the designated functions –
function dispatch(handler, pathname, req) { console.log("About to dispatch a request for " + pathname); var content = "Hey "+pathname; if (typeof handler[pathname] === 'function') { content += handler[pathname](req); } else { console.log("No request handler found for " + pathname); } return content; } exports.dispatch = dispatch;
That’s it, we now have built a fully functional static + dynamic Node server, it can serve both files from your drive as well as dynamic URLs.
Now just start your server
node index.js
Here is an output from my laptop ->
Static Page pipe’d from the server
Dynamic Content generated reading request
As usual, everything can be downloaded from Node JS Server.
PS: I am amazed by the speed Node serves you the content. Too Fast !! 🙂
Update : Updated the server to avoid dependencies of having a path for static files. View the post here.
12 Comments
blackhat · July 27, 2012 at 3:15 am
You completed a number of fine points there. I did a search on the issue and found mainly folks will consent with your blog.
Supal Dubey · July 27, 2012 at 1:32 pm
Thanks!
In case you liked it, you might also be interested in
http://cubestack.in/extending-our-nodejs-static-dynamic-server/
Clark · July 28, 2012 at 1:46 am
I do agree with all of the concepts you have presented for your post. They’re really convincing and can definitely work. Still, the posts are very brief for newbies. May you please prolong them a little from subsequent time? Thank you for the post.
mulberry uk · July 28, 2012 at 2:40 am
Its like you read my thoughts! You seem to know a lot approximately this, like you wrote the guide in it or something. I feel that you just could do with some p.c. to pressure the message home a little bit, but instead of that, this is wonderful blog. A great read. I’ll definitely be back.
Supal Dubey · July 28, 2012 at 7:18 am
@Clark – Thanks, just in case you are a newbie I would recommend you to start with
http://cubestack.in/your-first-nodejs-application/
Clark · July 30, 2012 at 3:59 am
Thanks! This was great
bags burberry online · July 30, 2012 at 12:51 am
It’s actually a nice and helpful piece of info. I’m happy that you simply shared this helpful information with us. Please stay us up to date like this. Thanks for sharing.
christian · August 1, 2012 at 12:24 pm
Hi to all, it’s in fact a pleasant for me to go to see this web site, it consists of important Information.
Victor · August 1, 2012 at 6:53 pm
whoah this weblog is excellent i love reading your posts. Keep up the good work! You realize, many persons are hunting around for this info, you could aid them greatly.
mulberry · August 2, 2012 at 8:02 am
Valuable info. Fortunate me I discovered your web site by accident, and I am shocked why this twist of fate didn’t came about in advance! I bookmarked it.
Longchamp · August 6, 2012 at 12:50 am
In these circumstances, has been expected, but the small courtyard of the people have changed, or so unhappy in his mind, a bit lostGrandmother room, there are a few books’s a The battle for a few minutes, the piece of tens of meters long ridges, small devils who became impassable valley of deathll show me yours post.
Supra · August 6, 2012 at 9:52 am
Sweet blog! I found it while browsing on Yahoo News. Do you have any tips on how to get listed in Yahoo News? I’ve been trying for a while but I never seem to get there! Thank you