Node Slides
Node Slides
Node Slides
JS
L E VE L O N E -
WHAT IS NODE.JS?
Allows you to build scalable network applications using JavaScript on the server-side. Node.js V8 JavaScript Runtime
e d co C ly st o m s it se u ca e Its fast b
INTRO TO NODE.JS
Websocket Server Like a Fast File Upload Client Ad Server Any Real-Time Data Apps
chat server
INTRO TO NODE.JS
A Web Framework For Beginners Its very low level Multi-threaded r e v r se d e ad e r h t e gl n si a You can think of it as
INTRO TO NODE.JS
Blocking Code
Read file from Filesystem, set equal to contents Print contents Do something else
Non-Blocking Code
Read file from Filesystem whenever youre complete, print the contents Do Something else
This is a Callback
INTRO TO NODE.JS
BLOCKING VS NON-BLOCKING
Blocking Code
var contents = fs.readFileSync('/etc/hosts'); console.log(contents); console.log('Doing something else');
e t e l p m o c l i t n u s s e c o r p Stop
Non-Blocking Code
fs.readFile('/etc/hosts', function(err, contents) { console.log(contents); }); console.log('Doing something else');
INTRO TO NODE.JS
Same as
var callback = function(err, contents) { console.log(contents); } fs.readFile('/etc/hosts', callback);
INTRO TO NODE.JS
BLOCKING VS NON-BLOCKING
var callback = function(err, contents) { console.log(contents); } fs.readFile('/etc/hosts', callback); fs.readFile('/etc/inetcfg', callback);
blocking 0s non-blocking 0s
INTRO TO NODE.JS
5s 5s
10s 10s
Status code in header response.write("Hello, this is dog."); Response body response.end(); Close the connection }).listen(8080); Listen for connections on this port
$ node hello.js
Starts the Event Loop when finished Run the Callback Known Events Checking for Events
request
WHY JAVASCRIPT?
JavaScript has certain characteristics that make it very different than other dynamic languages, namely that it has no concept of threads. Its model of concurrency is completely based around events. - Ryan Dahl
INTRO TO NODE.JS
5000ms = 5 seconds
INTRO TO NODE.JS
request timeout
INTRO TO NODE.JS
0s
5s
10s
Wasted Time
0s
5s
10s
INTRO TO NODE.JS
E VE NTS
L E VE L T WO -
The DOM triggers Events you can listen for those events
click submit
attach
When click event is triggered
EVENTS
events
hover
EVENTS IN NODE
net.Server EventEmitter fs.readStream EventEmitter
event
data
event
EVENTS
error
events
warn
info
EVENTS
EVENTS IN NODE
net.Server EventEmitter
attach
function(request, response){ .. }
event
EVENTS
BREAKING IT DOWN
http.createServer(function(request, response){ ... });
EVENTS
ALTERNATE SYNTAX
http.createServer(function(request, response){ ... });
Same as
var server = http.createServer(); server.on('request', function(request, response){ ... });
EVENTS
STREA MS
L EVE L TH R E E -
STREAMS
STREAMING RESPONSE
readable stream
response.writeHead(200); response.write("Dog is running."); setTimeout(function(){ response.write("Dog is done."); response.end(); }, 5000); }).listen(8080);
writable stream
http.createServer(function(request, response) {
emit
events
data end
STREAMS
STREAMS
STREAMS
STREAMS
UPLOAD A FILE
var fs = require('fs'); var http = require('http'); http.createServer(function(request, response) { var newFile = fs.createWriteStream("readme_copy.md"); request.pipe(newFile); request.on('end', function() { response.end('uploaded!'); }); }).listen(8080);
STREAMS
client
original file
0s
BACK PRESSURE!
server Writable stream slower than readable stream original file transferred file
client
storage
All encapsulated in
readStream.pipe(writeStream);
STREAMS
Outputs:
progress: progress: progress: progress: progress: ... progress: progress: 3% 6% 9% 12% 13% 99% 100%
STREAMS
DOCUMENTATION http://nodejs.org/api/
Stability Scores
STREAMS
STREAMS
STREAMS
SHOWING PROGRESS
STREAMS
MOD ULES
L EV E L FO U R -
REQUIRING MODULES
var http = require('http'); var fs = require('fs');
http.js fs.js
How does require return the libraries? How does it find these files?
MODULES
custom_goodbye.js
exports.goodbye = function() { console.log("bye!"); }
app.js
hello();
MODULES
app.js
var myMod = require('./my_module'); myMod.foo(); myMod.bar();
private
MODULES
app.js
var message = "Here's looking at you, kid."; var options = { host: 'localhost', port: 8080, path: '/', method: 'POST' } var request = http.request(options, function(response){ response.on('data', function(data){ console.log(data); }); });
request.write(message); request.end();
MODULES
app.js
var makeRequest = function(message) { var options = { host: 'localhost', port: 8080, path: '/', method: 'POST' } var request = http.request(options, function(response){ response.on('data', function(data){ console.log(data); }); });
Text
MODULES
make_request.js
var makeRequest = require('./make_request'); makeRequest("Here's looking at you, kid"); makeRequest("Hello, this is dog");
app.js
REQUIRE SEARCH
var make_request = require('./make_request') var make_request = require('../make_request') var make_request = require('/Users/eric/nodes/make_request')
/Home/eric/my_app/app.js
Package manager for node Comes with node Module Repository Dependency Management Easily publish modules Local Only
MODULES
https://github.com/mikeal/request
In /Home/my_app/app.js
var request = require('request');
MODULES
LOCAL VS GLOBAL
Install modules with executables globally
$ npm install coffee-script -g $ coffee app.coffee
global
MODULES
FINDING MODULES
npm registry npm command line
$ npm search request
MODULES
version number
MODULES
DEPENDENCIES
my_app/package.json
"dependencies": { "connect": "1.8.7" }
No conflicting modules!
Installs sub-dependencies
my_app node_modules connect
qs mime formidable
MODULES
SEMANTIC VERSIONING
"connect": "1.8.7" 1
Ranges
"connect": "~1" "connect": "~1.8" "connect": "~1.8.7" >=1.0.0 <2.0.0 >=1.8 <2.0.0 >=1.8.7 <1.9.0
http://semver.org/
MODULES
EX PRES S
L EV E L FI VE -
EXPRESS
Sinatra inspired web development framework for Node.js -insanely fast, flexible, and simple
Easy route URLs to callbacks Middleware (from Connect) Environment based configuration Redirection helpers File Uploads
EXPRESS
INTRODUCING EXPRESS
var express = require('express'); var app = express.createServer(); $ npm install express
root route
app.get('/', function(request, response) { response.sendfile(__dirname + "/index.html"); }); app.listen(8080); $ curl http://localhost:8080/ > 200 OK
current directory
EXPRESS
EXPRESS ROUTES
var request = require('request'); var url = require('url'); app.get('/tweets/:username', function(req, response) { var username = req.params.username; options = { protocol: "http:", host: 'api.twitter.com', pathname: '/1/statuses/user_timeline.json', query: { screen_name: username, count: 10} } var twitterUrl = url.format(options); request(twitterUrl).pipe(response); });
route definition
app.js
EXPRESS
EXPRESS ROUTES
EXPRESS
EXPRESS + HTML
EXPRESS
EXPRESS TEMPLATES
app.get('/tweets/:username', function(req, response) { ... request(url, function(err, res, body) { var tweets = JSON.parse(body); response.render('tweets.ejs', {tweets: tweets, name: username}); }); }); <h1>Tweets for @<%= name %></h1> <ul> <% tweets.forEach(function(tweet){ %> <li><%= tweet.text %></li> <% }); %> </ul>
app.js
tweets.ejs
EXPRESS
EXPRESS TEMPLATES
EXPRESS
TEMPLATE LAYOUTS
<h1>Tweets for @<%= name %></h1> <ul> <% tweets.forEach(function(tweet){ %> <li><%= tweet.text %></li> <% }); %> </ul>
tweets.ejs
<!DOCTYPE html> <html> <head> <title>Tweets</title> </head> <body> <%- body %> </body> </html>
layout.ejs
EXPRESS
EXPRESS TEMPLATES
EXPRESS
S OCKET. IO
L E VE L S IX -
CHATTR
SOCKET.IO
WEBSOCKETS
browser traditional server
Traditional request/response cycle
SOCKET.IO
WEBSOCKETS
browser socket.io
Using duplexed websocket connection
SOCKET.IO
app.js
index.html
SOCKET.IO
app.js
index.html
</script>
SOCKET.IO
SOCKET.IO
app.js
SOCKET.IO
SOCKET.IO
BROADCASTING MESSAGES
app.js
clients
socket.broadcast.emit("message", 'Hello');
server
SOCKET.IO
BROADCASTING MESSAGES
io.sockets.on('connection', function(client) { client.on('messages', function (data) { client.broadcast.emit("messages", data); }); });
<script>
app.js
...
</script>
SOCKET.IO
BROADCASTING MESSAGES
SOCKET.IO
app.js
var server = io.connect('http://localhost:8080'); server.on('connect', function(data) { $('#status').html('Connected to chattr'); nickname = prompt("What is your nickname?"); server.emit('join', nickname); });
</script>
SOCKET.IO
app.js
client.get('nickname', function(err, name) { client.broadcast.emit("chat", name + ": " + message); }); }); });
SOCKET.IO
SOCKET.IO
PE RS ISTING DATA
L EV E L SE VE N -
RECENT MESSAGES
PERSISTING DATA
RECENT MESSAGES
io.sockets.on('connection', function(client) { client.on('join', function(name) { client.set('nickname', name); client.broadcast.emit("chat", name + " joined the chat"); }); client.on("messages", function(message){ client.get("nickname", function(error, name) { client.broadcast.emit("messages", name + ": " + message); }); }); });
app.js
PERSISTING DATA
STORING MESSAGES
var messages = [];
app.js
var storeMessage = function(name, data){ messages.push({name: name, data: data}); if (messages.length > 10) { messages.shift(); } }
add message to end of array if more than 10 messages long, remove the last one
io.sockets.on('connection', function(client) { client.on("messages", function(message){ client.get("nickname", function(error, name) { storeMessage(name, message); }); }); });
PERSISTING DATA
EMITTING MESSAGES
io.sockets.on('connection', function(client) { ... client.on('join', function(name) { messages.forEach(function(message) { client.emit("messages", message.name + ": " + message.data); }); }); });
app.js
iterate through messages array and emit a message on the connecting client for each one
PERSISTING DATA
RECENT MESSAGES
PERSISTING DATA
PERSISTING STORES
All non-blocking!
commands
SET, GET, APPEND, DECR, INCR... HSET, HGET, HDEL, HGETALL...
LPUSH, LREM, LTRIM, RPOP, LINSERT...
PERSISTING DATA
PERSISTING DATA
NODE REDIS
PERSISTING DATA
REDIS
$ npm install redis var redis = require('redis'); var client = redis.createClient(); client.set("message1", "hello, yes this is dog"); client.set("message2", "hello, no this is spider");
key
value
PERSISTING DATA
PERSISTING DATA
app.js
PERSISTING DATA
CONVERTING STOREMESSAGE
var redisClient = redis.createClient(); var storeMessage = function(name, data){ var message = JSON.stringify({name: name, data: data});
app.js
PERSISTING DATA
app.js
PERSISTING DATA
app.js
reverse so they are emitted in correct order parse into JSON object
PERSISTING DATA
IN ACTION
PERSISTING DATA
PERSISTING DATA
ADDING CHATTERS
client.on('join', function(name){
app.js
notify other clients a chatter has joined add name to chatters set
index.html
PERSISTING DATA
app.js
emit all the currently logged in chatters to the newly connected client
PERSISTING DATA
REMOVING CHATTERS
client.on('disconnect', function(name){
app.js
client.get('nickname', function(err, name){ client.broadcast.emit("remove chatter", name); redisClient.srem("chatters", name); }); }); server.on('remove chatter', removeChatter);
index.html
PERSISTING DATA
WELCOME TO CHATTR
PERSISTING DATA