Introduction
JavaScript on the server isn't new. We've had Rhino, SpiderMonkey, and other JavaScript engines for years. But Node.js is different, and it's generating serious excitement.
Released last year by Ryan Dahl, Node.js takes Chrome's V8 JavaScript engine and wraps it with APIs for file I/O, networking, and other server-side tasks. The result is a platform for building scalable network applications using JavaScript.
Why JavaScript on the server? Because JavaScript developers can now write both client and server code. Because Node's event-driven model handles concurrency elegantly. Because it's fast.
Let's explore Node.js and see what the buzz is about.
What is Node.js?
Node.js is:
- JavaScript runtime built on Chrome's V8 engine
- Event-driven and non-blocking I/O model
- Single-threaded but highly concurrent
- Fast – V8 compiles JavaScript to machine code
Node.js is NOT:
- A web framework (but can build one)
- Multi-threaded
- The solution to every problem
- Production-ready yet (still early, version 0.2.x)
Think of Node as a platform for JavaScript applications, similar to how Ruby is a platform for Rails.
Why Node.js?
One language everywhere: JavaScript on client and server. Share code, share data structures, switch contexts seamlessly.
Event-driven concurrency: Handle thousands of concurrent connections without threads. No locks, no deadlocks, no threading complexity.
Fast: V8 is incredibly fast. Node performs well even compared to compiled languages.
npm (coming soon): Package manager for Node modules. Easy dependency management.
Active community: Rapid development, many modules, enthusiastic adoption.
Traditional vs Node.js
Traditional (Apache/PHP):
Request comes in → Spawn thread → Block on database → Wait → Respond
Each request gets a thread. Threads are expensive. Blocking I/O wastes CPU.
Node.js:
Request comes in → Register callback → Do something else
Database responds → Run callback → Send response
Single thread handles thousands of requests. Non-blocking I/O keeps CPU busy.
Example – File read:
Blocking (traditional):
var data = readFile('file.txt'); // Wait here
console.log(data);
console.log('Done');
Non-blocking (Node):
readFile('file.txt', function(data) {
console.log(data);
});
console.log('Done'); // Runs immediately
Node continues executing while waiting for I/O. Callbacks handle results when ready.
Installing Node.js
From source (Linux/Mac):
wget http://nodejs.org/dist/node-v0.2.5.tar.gz
tar xzf node-v0.2.5.tar.gz
cd node-v0.2.5
./configure
make
sudo make install
Test installation:
node --version
You should see the version number.
Hello World
Create hello.js:
console.log('Hello World!');
Run it:
node hello.js
Output: Hello World!
That's it. JavaScript running on the server.
A Simple HTTP Server
Node's real power is networking. Create server.js:
var http = require('http');
var server = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
});
server.listen(8000);
console.log('Server running at http://localhost:8000/');
Run it:
node server.js
Visit http://localhost:8000/ and you'll see "Hello World".
That's a complete web server in 8 lines of code.
Compare to Apache+PHP configuration. Node makes servers trivial.
Understanding the Event Loop
Node runs a single-threaded event loop:
// Start server
server.listen(8000);
// Event loop starts
while (true) {
// Wait for event
var event = getNextEvent();
// Execute callback
event.callback();
}
Everything is asynchronous:
setTimeout(function() {
console.log('After 1 second');
}, 1000);
console.log('Immediately');
// Output:
// Immediately
// After 1 second
The event loop continues while waiting. No blocking, no wasted CPU.
Reading Files
Asynchronous (preferred):
var fs = require('fs');
fs.readFile('file.txt', 'utf8', function(err, data) {
if (err) throw err;
console.log(data);
});
console.log('Reading file...');
Synchronous (avoid in production):
var fs = require('fs');
var data = fs.readFileSync('file.txt', 'utf8');
console.log(data);
Synchronous blocks the event loop. Use only at startup.
Serving Static Files
Real web server needs to serve files:
var http = require('http');
var fs = require('fs');
http.createServer(function(req, res) {
fs.readFile('index.html', function(err, data) {
if (err) {
res.writeHead(404);
res.end('Not Found');
return;
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data);
});
}).listen(8000);
This reads and serves index.html for every request.
Routing Requests
Handle different URLs:
var http = require('http');
var url = require('url');
http.createServer(function(req, res) {
var pathname = url.parse(req.url).pathname;
if (pathname === '/') {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Home Page');
} else if (pathname === '/about') {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('About Page');
} else {
res.writeHead(404);
res.end('Not Found');
}
}).listen(8000);
Now different URLs serve different content.
Handling POST Data
Receive form submissions:
var http = require('http');
var querystring = require('querystring');
http.createServer(function(req, res) {
if (req.method === 'POST') {
var body = '';
req.on('data', function(chunk) {
body += chunk;
});
req.on('end', function() {
var data = querystring.parse(body);
res.end('Received: ' + data.name);
});
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<form method="post">' +
'<input name="name">' +
'<button>Submit</button>' +
'</form>');
}
}).listen(8000);
Data arrives in chunks. Collect chunks in the 'data' event, process in 'end' event.
Modules
Node uses CommonJS modules:
math.js:
exports.add = function(a, b) {
return a + b;
};
exports.multiply = function(a, b) {
return a * b;
};
app.js:
var math = require('./math');
console.log(math.add(2, 3)); // 5
console.log(math.multiply(2, 3)); // 6
Organize code into reusable modules.
Alternative export style:
module.exports = function() {
return {
add: function(a, b) { return a + b; },
multiply: function(a, b) { return a * b; }
};
};
Built-in Modules
Node includes many modules:
http – HTTP server/client fs – File system operations path – File path utilities url – URL parsing querystring – Query string parsing events – Event emitter stream – Streaming data crypto – Cryptography util – Utility functions
Example – path module:
var path = require('path');
console.log(path.basename('/foo/bar/file.txt')); // file.txt
console.log(path.dirname('/foo/bar/file.txt')); // /foo/bar
console.log(path.extname('/foo/bar/file.txt')); // .txt
Event Emitters
Many Node objects are EventEmitters:
var events = require('events');
var emitter = new events.EventEmitter();
// Register listener
emitter.on('data', function(data) {
console.log('Received: ' + data);
});
// Emit event
emitter.emit('data', 'Hello World');
This pattern is fundamental to Node.
HTTP server is an EventEmitter:
var server = http.createServer();
server.on('request', function(req, res) {
res.end('Hello World');
});
server.listen(8000);
Streams
Streams handle data efficiently:
var fs = require('fs');
// Read stream
var readStream = fs.createReadStream('input.txt');
// Write stream
var writeStream = fs.createWriteStream('output.txt');
// Pipe data
readStream.pipe(writeStream);
Streams process data in chunks without loading everything into memory.
HTTP response is a stream:
http.createServer(function(req, res) {
var stream = fs.createReadStream('large-file.txt');
stream.pipe(res);
}).listen(8000);
Serve large files without memory issues.
Error Handling
Always handle errors:
fs.readFile('file.txt', function(err, data) {
if (err) {
console.error('Error:', err);
return;
}
console.log(data);
});
Uncaught exceptions crash Node:
process.on('uncaughtException', function(err) {
console.error('Uncaught exception:', err);
process.exit(1);
});
Handle errors gracefully to avoid crashes.
Building a Simple App
Let's build a URL shortener:
var http = require('http');
var url = require('url');
var urls = {}; // In-memory storage
var counter = 1;
http.createServer(function(req, res) {
var pathname = url.parse(req.url).pathname;
var query = url.parse(req.url, true).query;
// Home page - show form
if (pathname === '/') {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<h1>URL Shortener</h1>' +
'<form action="/shorten" method="get">' +
'<input name="url" placeholder="Enter URL">' +
'<button>Shorten</button>' +
'</form>');
}
// Shorten URL
else if (pathname === '/shorten') {
var longUrl = query.url;
var id = counter++;
urls[id] = longUrl;
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('Short URL: <a href="/' + id + '">/' + id + '</a>');
}
// Redirect to long URL
else {
var id = pathname.substring(1);
var longUrl = urls[id];
if (longUrl) {
res.writeHead(302, {'Location': longUrl});
res.end();
} else {
res.writeHead(404);
res.end('Not Found');
}
}
}).listen(8000);
console.log('URL Shortener running at http://localhost:8000/');
A functional URL shortener in ~40 lines.
Node vs Other Platforms
Node.js vs PHP:
- Node is non-blocking, PHP is blocking
- Node uses JavaScript, PHP uses PHP
- Node requires process management, PHP works with Apache
Node.js vs Ruby/Python:
- Similar scripting languages
- Node is single-threaded event-driven, Ruby/Python typically use threads
- Node emphasizes asynchronous I/O
Node.js vs Java/C#:
- Node is simpler for certain tasks
- Java/C# better for CPU-intensive work
- Node excels at I/O-bound applications
When to Use Node.js
Good fit:
- Real-time applications (chat, gaming)
- API servers
- Data streaming
- I/O-heavy applications
- Microservices
- Single-page application backends
Poor fit:
- CPU-intensive calculations
- Traditional web apps with server-side rendering
- Environments requiring stability (Node is young)
Node shines for I/O-bound, event-driven applications.
The Node Ecosystem
Coming soon: npm (Node Package Manager)
npm will make installing modules easy:
npm install express
npm install socket.io
Community is building modules rapidly:
- Express – Web framework
- Socket.IO – WebSocket library
- Connect – Middleware framework
- Jade – Template engine
- Mongoose – MongoDB ODM
The ecosystem is growing fast.
Learning Resources
Node.js website: http://nodejs.org/
Node.js Manual: http://nodejs.org/api.html
HowToNode: http://howtonode.org/ – Tutorials and articles
Node.js Google Group: Active community for questions
GitHub: http://github.com/joyent/node – Source code
Common Pitfalls
Callback hell: Nested callbacks get messy:
fs.readFile('file1', function(err, data1) {
fs.readFile('file2', function(err, data2) {
fs.readFile('file3', function(err, data3) {
// Getting deep...
});
});
});
Use named functions or libraries to manage this.
Blocking the event loop: Don't do CPU-intensive work in the main thread. Use child processes or worker queues.
Not handling errors: Always handle callback errors. Uncaught exceptions crash Node.
Production Considerations
Node.js is young (version 0.2.x). For production:
Process management: Use tools to restart Node if it crashes
Reverse proxy: Put nginx in front of Node
Error handling: Log everything, handle all errors
Testing: Test thoroughly, Node changes fast
Clustering: Run multiple Node processes
Many companies are using Node in production (Yahoo, LinkedIn), but be aware of the risks.
The Future of Node
Node is evolving rapidly. Upcoming features:
- npm package manager
- Better Windows support
- Improved stability
- Growing ecosystem
The community is vibrant. New modules appear daily. Conference talks generate excitement. Node is gaining momentum.
Wrapping Up
Node.js represents a new approach to server-side development. Event-driven, non-blocking I/O handles concurrency elegantly. JavaScript everywhere reduces context switching. The platform is fast and efficient.
It's early days. Node isn't production-ready for everything. The API changes. Documentation is sparse. But the potential is clear.
If you're building real-time applications, API servers, or I/O-heavy services, Node deserves serious consideration. The event-driven model fits these use cases perfectly.
Start experimenting. Build a simple server. Try handling WebSockets. Explore the ecosystem. Node's approach to concurrency is different from threads, and understanding it takes practice.
JavaScript on the server isn't just possible – with Node.js, it's powerful. The future of server-side JavaScript looks bright.
Give Node.js a try. You might find it changes how you think about building network applications.