Building servers with Node.js

135
Building Servers with Node.js Travis Swicegood Licensed under Creative Commons, Attribution, Share-Alike #nodetalk

Transcript of Building servers with Node.js

Building Serverswith Node.js

Travis Swicegood

Licensed under Creative Commons,Attribution, Share-Alike

#nodetalk

some rights reservedbuilding servers with node.js

Hi, I’m Travis

some rights reservedbuilding servers with node.js

#nodetalk

some rights reservedbuilding servers with node.js

What’s thistalk about?

some rights reservedbuilding servers with node.js

What isNode.js?

some rights reservedbuilding servers with node.js

Servers

some rights reservedbuilding servers with node.js

GTD

some rights reservedbuilding servers with node.js

Is it RightFor Me?

some rights reservedbuilding servers with node.js

What isNode.js?

some rights reservedbuilding servers with node.js

Evented I/ONetworking

Toolkit

some rights reservedbuilding servers with node.js

What’s “evented?”

some rights reservedbuilding servers with node.js

Some Code

some rights reservedbuilding servers with node.js

var db = new Database({host: "localhost"});db.connect();var result = db.find({name: "a-post"});// do something with result

Standard Data Store

some rights reservedbuilding servers with node.js

var db = new Database({host: "localhost"});db.connect(function(conn) { conn.find({name: "a-post"}, function(result) { // do something with result });});

Evented Data Store

some rights reservedbuilding servers with node.js

Event Loop

some rights reservedbuilding servers with node.js

An Analogy

some rights reservedbuilding servers with node.js

A rabbi, a priest, and a cowboy walk

into a bar.

some rights reservedbuilding servers with node.js

Take 1Simple Bartender

some rights reservedbuilding servers with node.js

Rabbi

some rights reservedbuilding servers with node.js

Priest

some rights reservedbuilding servers with node.js

Cowboy…

some rights reservedbuilding servers with node.js

… he startsto talk

some rights reservedbuilding servers with node.js

Bartender isnow blocked

some rights reservedbuilding servers with node.js

Take 2Evented Bartender

some rights reservedbuilding servers with node.js

Rabbi

some rights reservedbuilding servers with node.js

Priest

some rights reservedbuilding servers with node.js

Cowboy…

some rights reservedbuilding servers with node.js

… he startsto talk

some rights reservedbuilding servers with node.js

Bartender startsfilling orders

some rights reservedbuilding servers with node.js

Fixing Take 1

some rights reservedbuilding servers with node.js

Add more bartenders

some rights reservedbuilding servers with node.js

Limit time with each client

some rights reservedbuilding servers with node.js

Respond toEvents

some rights reservedbuilding servers with node.js

Who uses this?

some rights reservedbuilding servers with node.js

Nginx

some rights reservedbuilding servers with node.js

Memcached

some rights reservedbuilding servers with node.js

Most NoSQL

some rights reservedbuilding servers with node.js

Servers

some rights reservedbuilding servers with node.js

Basic Socket Server

some rights reservedbuilding servers with node.js

var net = require("net");net.createServer(function(socket) { socket.on("data", function(data) { socket.write("You said: " + data); socket.end(); }); socket.write("Please say something:\n");}).listen(1234);console.log("Listening on 1234");

Echo Server

some rights reservedbuilding servers with node.js

var net = require("net");net.createServer(function(socket) { socket.on("data", function(data) { socket.write("You said: " + data); socket.end(); }); socket.write("Please say something:\n");}).listen(1234);console.log("Listening on 1234");

Echo Server

some rights reservedbuilding servers with node.js

var net = require("net");net.createServer(function(socket) { socket.on("data", function(data) { socket.write("You said: " + data); socket.end(); }); socket.write("Please say something:\n");}).listen(1234);console.log("Listening on 1234");

Echo Server

some rights reservedbuilding servers with node.js

var net = require("net");net.createServer(function(socket) { socket.on("data", function(data) { socket.write("You said: " + data); socket.end(); }); socket.write("Please say something:\n");}).listen(1234);console.log("Listening on 1234");

Echo Server

some rights reservedbuilding servers with node.js

var net = require("net");net.createServer(function(socket) { socket.on("data", function(data) { socket.write("You said: " + data); socket.end(); }); socket.write("Please say something:\n");}).listen(1234);console.log("Listening on 1234");

Echo Server

some rights reservedbuilding servers with node.js

var net = require("net");net.createServer(function(socket) { socket.on("data", function(data) { socket.write("You said: " + data); socket.end(); }); socket.write("Please say something:\n");}).listen(1234);console.log("Listening on 1234");

Echo Server

some rights reservedbuilding servers with node.js

var net = require("net");net.createServer(function(socket) { socket.on("data", function(data) { socket.write("You said: " + data); socket.end(); }); socket.write("Please say something:\n");}).listen(1234);console.log("Listening on 1234");

Echo Server

some rights reservedbuilding servers with node.js

Storing State

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

net.createServer(function(socket) { var name = false; socket.on("data", function(data) { if (!name) { name = data.toString('utf8').trim(); socket.write("Thanks, " + name + "\n"); socket.write("Please say something:\n"); } else { socket.write(name + ", you said: " + data); socket.end(); } }); socket.write("Please tell me your name: ");}).listen(1234);

Personalized Echo Server

some rights reservedbuilding servers with node.js

Web Servers

some rights reservedbuilding servers with node.js

Hello World

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end();}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end();}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end();}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end();}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end();}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end();}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

Something’sDifferent

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end();}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end();}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

Any idea why?

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { setTimeout(function() { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>Hello, World!</h1>"); response.end(); }, 1000);}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { db.find({"slug": "a-blog-post"}, function(post) { response.writeHead(200, {"Content-Type": "text/html"}); response.write("<h1>" + post.title + "</h1>"); response.write(post.body); response.end(); });}).listen(4321);console.log("Now listening on port 4321");

Hello World

some rights reservedbuilding servers with node.js

Grokkingthe Request

some rights reservedbuilding servers with node.js

Output the Request

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write(require("util").inspect(request)); response.end();}).listen(4321);console.log("Listening on 4321");

Output the Request

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.write(require("util").inspect(request)); response.end();}).listen(4321);console.log("Listening on 4321");

Output the Request

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/ { socket: { bufferSize: 0, fd: 7, type: 'tcp4', allowHalfOpen: true, _readWatcher: { socket: [Circular], callback: [Function: onReadable] }, readable: true, _writeQueue: [], _writeQueueEncoding: [], _writeQueueFD: [],… and so on, and so on …

Output the Request

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/ … way down around line 148 … url: '/', method: 'GET',

Output the Request

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/say?msg=Hello | grep "url:" url: '/say?msg=Hello',

Output the Request

some rights reservedbuilding servers with node.js

Parsing the URL

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); var parsedUrl = require("url").parse(request.url); response.write(require("util").inspect(parsedUrl)); response.end();}).listen(4321);console.log("Listening on 4321");

Parsing the URL

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/say?msg=Hello { href: '/say?msg=Hello', search: '?msg=Hello', query: 'msg=Hello', pathname: '/say' }

Parsing the URL

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/say?msg=Hello { href: '/say?msg=Hello', search: '?msg=Hello', query: 'msg=Hello', pathname: '/say' }

Parsing the URL

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/say?msg=Hello { href: '/say?msg=Hello', search: '?msg=Hello', query: 'msg=Hello', pathname: '/say' }

Parsing the URL

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/say?msg=Hello { href: '/say?msg=Hello', search: '?msg=Hello', query: 'msg=Hello', pathname: '/say' }

Parsing the URL

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/say?msg=Hello { href: '/say?msg=Hello', search: '?msg=Hello', query: 'msg=Hello', pathname: '/say' }

Parsing the URL

some rights reservedbuilding servers with node.js

Parsing the Query

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/say?msg=Hello { href: '/say?msg=Hello', search: '?msg=Hello', query: 'msg=Hello', pathname: '/say' }

Parsing the Query

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); var parsedUrl = require("url").parse(request.url, true); response.write(require("util").inspect(parsedUrl)); response.end();}).listen(4321);console.log("Listening on 4321");

Parsing the Query

some rights reservedbuilding servers with node.js

var http = require("http");http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); var parsedUrl = require("url").parse(request.url, true); response.write(require("util").inspect(parsedUrl)); response.end();}).listen(4321);console.log("Listening on 4321");

Parsing the Query

some rights reservedbuilding servers with node.js

$ curl -s http://localhost:4321/say?msg=Hello { href: '/say?msg=Hello', search: '?msg=Hello', query: { msg: 'Hello' }, pathname: '/say' }

Parsing the Query

some rights reservedbuilding servers with node.js

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

function(request, response) { var raw = ""; request.on("data", function(chunk) { raw += chunk; }); request.on("end", function() { var data = JSON.parse(raw), obj = {message: data.message.split("").reverse().join("")}; response.writeHead(200, {"Content-Type": "application/json"}); response.write(JSON.stringify(obj)); response.end(); });}

Receiving Data

some rights reservedbuilding servers with node.js

Somethingis Wrong

some rights reservedbuilding servers with node.js

$ node json-reverse.js Listening on 4321

undefined:0^SyntaxError: Unexpected end of input at Object.parse (native) at IncomingMessage.<anonymous> (/Book/code/servers/json-reverse.js:8:21) at IncomingMessage.emit (events.js:39:17) at HTTPParser.onMessageComplete (http.js:111:23) at Socket.ondata (http.js:945:22) at Socket._onReadable (net.js:654:27) at IOWatcher.onReadable [as callback] (net.js:156:10)

Can’t handle Errors

some rights reservedbuilding servers with node.js

Fault-Tolerance

some rights reservedbuilding servers with node.js

var handleResponse = function(response, code, message) { response.writeHead(code, {"Content-Type": "application/json"}); response.write(message); response.end();};

Quick Refactor

some rights reservedbuilding servers with node.js

request.on("end", function() { if (!raw) { handleResponse(response, 400, "Requires more data"); return; } try { var data = JSON.parse(raw); } catch(e) { handleResponse(response, 400, "Invalid JSON"); return; } if (!data.message) { handleResponse(response, 400, "Requires a message"); return; }

var obj = {message: data.message.split("").reverse().join("") }; handleResponse(response, 200, JSON.stringify(obj)) });

Fault Tolerant Reverse

some rights reservedbuilding servers with node.js

request.on("end", function() { if (!raw) { handleResponse(response, 400, "Requires more data"); return; } try { var data = JSON.parse(raw); } catch(e) { handleResponse(response, 400, "Invalid JSON"); return; } if (!data.message) { handleResponse(response, 400, "Requires a message");

Fault Tolerant Reverse

some rights reservedbuilding servers with node.js

request.on("end", function() { if (!raw) { handleResponse(response, 400, "Requires more data"); return; } try { var data = JSON.parse(raw); } catch(e) { handleResponse(response, 400, "Invalid JSON"); return; } if (!data.message) { handleResponse(response, 400, "Requires a message");

Fault Tolerant Reverse

some rights reservedbuilding servers with node.js

// earlier code } catch(e) { handleResponse(response, 400, "Invalid JSON"); return; } if (!data.message) { handleResponse(response, 400, "Requires a message"); return; }

var obj = {message: data.message.split("").reverse().join("") }; handleResponse(response, 200, JSON.stringify(obj)) });

Fault Tolerant Reverse

some rights reservedbuilding servers with node.js

// earlier code } catch(e) { handleResponse(response, 400, "Invalid JSON"); return; } if (!data.message) { handleResponse(response, 400, "Requires a message"); return; }

var obj = {message: data.message.split("").reverse().join("") }; handleResponse(response, 200, JSON.stringify(obj)) });

Fault Tolerant Reverse

some rights reservedbuilding servers with node.js

GTD

some rights reservedbuilding servers with node.js

Libraries Help

some rights reservedbuilding servers with node.js

Expressvisionmedia/express

some rights reservedbuilding servers with node.js

var app = express.createServer();

app.get('/', function(req, res){ res.send('Hello World');});

app.listen(3000);

Hello World

some rights reservedbuilding servers with node.js

Socket.IOhttp://socket.io/

some rights reservedbuilding servers with node.js

var http = require('http'),      io = require('socket.io');

var server = http.createServer(function(req, res){  // your normal server code  res.writeHead(200, {'Content-Type': 'text/html'});  res.end('<h1>Hello world</h1>'); });server.listen(80);  // socket.io var socket = io.listen(server); socket.on('connection', function(client){   // new client is here!   client.on('message', function(){ … })   client.on('disconnect', function(){ … }) });

Push from the Server

some rights reservedbuilding servers with node.js

// socket.io var socket = io.listen(server); socket.on('connection', function(client){   // new client is here!   client.on('message', function(){ … })   client.on('disconnect', function(){ … }) });

Socket.IO

some rights reservedbuilding servers with node.js

// socket.io var socket = io.listen(server); socket.on('connection', function(client) { // send a message back to the client client.send({msg: "Thanks for connecting…"}); // broadcast a message to everyone client.broadcast({msg: "Another one connected!"});});

Sending to Client

some rights reservedbuilding servers with node.js

CoffeeScriptjashkenas/coffee-script

some rights reservedbuilding servers with node.js

request.on("end", function() { if (!raw) { handleResponse(response, 400, "Requires more data"); return; } try { var data = JSON.parse(raw); } catch(e) { handleResponse(response, 400, "Invalid JSON"); return; } if (!data.message) { handleResponse(response, 400, "Requires a message"); return; }

var obj = {message: data.message.split("").reverse().join("") }; handleResponse(response, 200, JSON.stringify(obj)) });

Remember this code?

some rights reservedbuilding servers with node.js

request.on "end", () -> return handleResponse response, 400, "Requires more data" unless raw

try data = JSON.parse raw catch e return handleResponse response, 400, "Requires more data"

return handleResponse response, 400, "Requires a message" unless data.message

obj = message: data.message.split("").reverse().join("") handleResponse response, 200, JSON.stringify obj

In CoffeeScript

some rights reservedbuilding servers with node.js

Vowscloudhead/vows

http://vowjs.org/

some rights reservedbuilding servers with node.js

No Samples

some rights reservedbuilding servers with node.js

Is it RightFor Me?

some rights reservedbuilding servers with node.js

It Depends

some rights reservedbuilding servers with node.js

The Checklist

some rights reservedbuilding servers with node.js

Controlthe Stack

some rights reservedbuilding servers with node.js

Comfortablewith Change

some rights reservedbuilding servers with node.js

Full Control OverApp Logic

some rights reservedbuilding servers with node.js

Lots of SmallIndependent

Pieces

some rights reservedbuilding servers with node.js

Then Maybe

some rights reservedbuilding servers with node.js

Where to?

some rights reservedbuilding servers with node.js

nodejs.org

some rights reservedbuilding servers with node.js

#Node.js (freenode)

some rights reservedbuilding servers with node.js

Travis Swicegoodtravisswicegood.com

@[email protected]://joind.in/2810