Successfully reported this slideshow.
Your SlideShare is downloading. ×

OSCON 2011 - Node.js Tutorial

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 173 Ad

More Related Content

Slideshows for you (20)

Similar to OSCON 2011 - Node.js Tutorial (20)

Advertisement

More from Tom Croucher (20)

Recently uploaded (20)

Advertisement

OSCON 2011 - Node.js Tutorial

  1. 1. Node.js Workshop Tom Hughes-Croucher Chief Evangelist / Node Tech Lead @sh1mmer tom@joyent.com
  2. 2. Overview • Introduction • Why Server-Side JavaScript? • What is Node? • Using Node • Understanding Node • Node Ecosystem • Programming Style • More Complex applications • Deploying Node apps to the cloud
  3. 3. Introduction • Tom Hughes-Croucher • Chief Evangelist at Joyent • Node.js core contributor • Author of "Up and Running with Node.js"
  4. 4. Scalable Server-Side Code with JavaScript Node Up and Running Tom Hughes-Croucher
  5. 5. Major update this week
  6. 6. Why Server-Side JavaScript?
  7. 7. JavaScript programmers 3>2>1
  8. 8. Massive Code base of jQuery and other JS libraries
  9. 9. Laziness or “I’m sick of writing stuff twice” I could have said efficiency, but I think we all secretly long to sit around in our underwear.
  10. 10. Progressive Enhancement is free* Remember WWCD (What Would Crockford Do) *close enough
  11. 11. TL;DR: SSJS is Awesome Like a Unicorn riding a Narwhal
  12. 12. If SSJS is so awesome why is it "new"?
  13. 13. 1. Professionalism
  14. 14. “Yahoo!'s corporate motto is: Don't be eval().“
  15. 15. 2. JavaScript Runtimes
  16. 16. Runtimes • V8 (Google), C++ • Spider Monkey (Mozilla), C++ • Rhino (Mozilla), Java
  17. 17. V8 Spider Monkey JavaScript Performance
  18. 18. 8x Sep 08! Mar 11!
  19. 19. Anatomy of SSJS
  20. 20. Node { !
  21. 21. Runtime != Browser
  22. 22. No DOM (By default, anyway)
  23. 23. Summary • Benefits of SSJS • Lots of JavaScript expertise • Lots of web code in JS libraries • Write once, run anywhere • Progressive Enhancement • Why SSJS happened now • Professionalism in JavaScript • New generation of JavaScript runtimes
  24. 24. What is Node?
  25. 25. Node • JavaScript programming environment • Uses V8 runtime • Event Driven • Non-blocking libraries • Supports CommonJS module format • Supports C/C++ based add-ons
  26. 26. Woah! Overload.
  27. 27. 1. It's JavaScript
  28. 28. See Above.
  29. 29. 2. It's Fast
  30. 30. concurrency=300, Smaller is Better 400 300 response time (ms) server nginx 200 thin tornado node_buffer 100 24 26 28 210 212 214 216 218 response size (bytes)
  31. 31. 3. It's easy to extend
  32. 32. 'Modules' in JS 'Add-ons' in C
  33. 33. 4. Node is _not_ Rails/ Django/etc
  34. 34. Node is bare-bone to the metal
  35. 35. However, the Node community are making Rails/Django/etc
  36. 36. 5. Node is young
  37. 37. Stable is "stable" Unstable moves fast
  38. 38. Using Node
  39. 39. Using Node • Part 1. Installation • Part 2. Basics • Part 3. Getting stuck in
  40. 40. Part 1. Installation
  41. 41. Nave a.k.a the easy way
  42. 42. Enki:~ $ wget -q http://github.com/isaacs/nave/raw/ master/nave.sh Enki:~ $ chmod 755 nave.sh Enki:~ $ ./nave.sh install latest
  43. 43. Nave • Installs and versions Node • Allows Node shells with specific versions • Allows you to get 'latest' <-- Stable • May add 'unstable' option in future
  44. 44. Manual Installation
  45. 45. Go to http://nodejs.org/#download and get the URL of the current stable release
  46. 46. Enki:~ $ wget -q http://nodejs.org/dist/node- v0.4.10.tar.gz Enki:~ $ tar xzf node-v0.4.10.tar.gz Enki:~ $ cd node-v0.4.10 Enki:~/node-v0.4.10 $
  47. 47. Local or system?
  48. 48. Local
  49. 49. Enki:~/node-v0.4.10 $ mkdir ~/local Enki:~/node-v0.4.10 $ ./configure --prefix=~/local Checking for program g++ or c++ : /usr/bin/g++ Checking for program cpp : /usr/bin/cpp ... Checking for fdatasync(2) with c++ : no 'configure' finished successfully (3.466s)
  50. 50. Enki:~/node-v0.4.10 $ make Waf: Entering directory `/Users/sh1mmer/node-v0.4.10/build' DEST_OS: darwin DEST_CPU: x86 Parallel Jobs: 1 [ 1/69] cc: deps/libeio/eio.c -> build/default/deps/libeio/eio_1.o /usr/bin/gcc -rdynamic -D_GNU_SOURCE -DHAVE_CONFIG_H=1 -DEV_MULTIPLICITY=0 -pthread -g -O3 -DHAVE_OPENSSL=1 - DX_STACKSIZE=65536 -D_LARGEFILE_SOURCE - D_FILE_OFFSET_BITS=64 -DHAVE_FDATASYNC=0 - DPLATFORM="darwin" -DNDEBUG -Idefault/deps/libeio -I../deps/ libeio -Idefault -I.. ../deps/libeio/eio.c -c -o default/deps/libeio/ eio_1.o ...
  51. 51. Enki:~/node-v0.4.10 $ make install Waf: Entering directory `/Users/sh1mmer/node-v0.4.10/build' DEST_OS: darwin DEST_CPU: x86 Parallel Jobs: 1 * installing build/default/config.h as /Users/sh1mmer/local/ include/node/config.h * installing build/default/node as /Users/sh1mmer/local/bin/ node * installing build/default/src/node_config.h as /Users/sh1mmer/ local/include/node/node_config.h Waf: Leaving directory `/Users/sh1mmer/node-v0.4.10/build' 'install' finished successfully (0.373s) Enki:~/node-v0.4.7 $
  52. 52. Enki:~/node-v0.4.10 $ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/ local/bin:/usr/X11/bin:/usr/local/git/bin:/Users/ croucher/Code/narwhal/bin:/opt/local/bin:/usr/local/ git/bin:/Users/sh1mmer/bin Enki:~/node-v0.4.10 $ node -v -bash: node: command not found Enki:~/node-v0.4.10 $ echo PATH=~/local/bin:$PATH >> ~/.profile Enki:~/node-v0.4.10 $ node -v v0.4.10
  53. 53. System
  54. 54. Enki:~/node-v0.4.10 $ ./configure ... Enki:~/node-v0.4.10 $ make ... Enki:~/node-v0.4.10 $ sudo make install ... Enki:~/node-v0.4.10 $ node -v v0.4.7 Enki:~/node-v0.4.10 $
  55. 55. Exercise • Get Node head from Github using Git • Install to ~/node
  56. 56. Part 2. Basics
  57. 57. node-repl Interactive JavaScript terminal
  58. 58. $Enki:~ $ node
  59. 59. $Enki:~ $ node >3>2>1 false > true == 1 true > true === 1 false
  60. 60. > console.log('Hello World'); Hello World > .help .clear Break, and also clear the local context. .exit Exit the prompt .help Show repl options > .clear Clearing context... > .exit Enki:~ $
  61. 61. Enki:~ $ node > var foo = "bar"; > foo; 'bar' > .clear Clearing context... > foo ReferenceError: foo is not defined at [object Context]:1:1 at Interface.<anonymous> (repl:98:19) at Interface.emit (events:27:15) at Interface._ttyWrite (readline:295:12) at Interface.write (readline:132:30) at Stream.<anonymous> (repl:79:9) at Stream.emit (events:27:15) at IOWatcher.callback (net:489:16)
  62. 62. var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello Worldn'); }).listen(8124, "127.0.0.1"); console.log('Server running at http://127.0.0.1:8124/');
  63. 63. var http = require('http'); //include the http library
  64. 64. http.createServer(function (req, res) { }).listen(8124, "127.0.0.1"); //create an http server //when ‘stuff’ happens call this anonymous function //listen on port 8124 of the IP 127.0.0.1
  65. 65. http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello Worldn'); }) //when ‘stuff’ happens my function fires //I get a request object and a response object //I write to the response object header //HTTP status 200 and content-type ‘text/plain’ //close the response with the body: //Hello World
  66. 66. console.log('Server running at http://127.0.0.1:8124/'); //write Server is running at http://127.0.0.1:8124/ //to the console
  67. 67. Interactive Debugging
  68. 68. Enki:~/Code/node-examples $ node --debug helloworld.js debugger listening on port 5858 Server running at http://127.0.0.1:8124/
  69. 69. Enki:~ $ npm install node-inspector node-inspector@0.1.8 ./node_modules/node-inspector websocket-server@1.4.04 paperboy@0.0.2 Enki:~ $ node-inspector visit http://0.0.0.0:8080/debug?port=5858 to start debugging
  70. 70. Exercises • Modify the HTTP server to return the text "I'm learning Node" • Change the HTTP response to HTML and return your text in an HTML page • Return the User-Agent string from the browser as part of your HTML page • Return different textual responses to 2 (or more) browsers
  71. 71. Part 3. Getting Stuck In
  72. 72. HTTP Client
  73. 73. var http = require('http'); var request = http.request({'host': 'www.google.com', 'port': 80, 'path': '/', 'method':'GET'}); request.on('response', function (response) { console.log('STATUS: ' + response.statusCode); console.log('HEADERS: ' + JSON.stringify(response.headers)); response.setEncoding('utf8'); response.on('data', function (chunk) { console.log('BODY: ' + chunk); });
  74. 74. Streaming API
  75. 75. write(data) write(data) Destination Request end() (google.com) response(headers) data(chunk) data(chunk) Response data(chunk) end()
  76. 76. Exercise • Fetch the NYTimes.com and output the contents to the console • Create a web server • Create an HTTP client • POST data to your web server • Output the POST data to console
  77. 77. Events
  78. 78. object.on('event', function() { //stuff } );
  79. 79. EventEmitter
  80. 80. EventEmitter • manage "event handlers" • list of functions to be called per event • provide mechanism to trigger events
  81. 81. var util = require('util'), EE = require('events').EventEmitter; util.inherits(MyClass, EE); var myObj = new MyClass(); //nb using first class functions myObj.on('something', function);
  82. 82. exports.inherits = function (ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false } }); };
  83. 83. More than just core
  84. 84. CommonJS Modules
  85. 85. Library format for SSJS
  86. 86. math.js exports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum; }; increment.js var add = require('math').add; exports.increment = function(val) { return add(val, 1); }; var inc = require('increment').increment; var a = 1; inc(a); // 2
  87. 87. Protip exports.awesome = function() { //yay my code is awesomesauce }; var exports.fail = function() { //exports is a global //by redeclaring it as //a local variable //your code _will_ fail };
  88. 88. Exercise • Create a CommonJS module called "fish" • Provide functions to: • swim • mouth breath • flop around • Import your module into a node project • Call the various functions
  89. 89. Node Package Manager (NPM)
  90. 90. NPM is written in JavaScript for Node
  91. 91. Enki:~ $ cat `which npm` #!/usr/bin/env node ;(function () { // wrapper in case we're in module_context mode var log = require("../lib/utils/log") log.waitForConfig() log.info("ok", "it worked if it ends with") var fs = require("../lib/utils/graceful-fs") , path = require("path") , sys = require("../lib/utils/sys") , npm = require("../npm") , ini = require("../lib/utils/ini") , rm = require("../lib/utils/rm-rf")
  92. 92. Enki:~/Code/node(master) $ npm install express express@2.3.11 ../../node_modules/express mime@1.2.2 connect@1.4.2 qs@0.1.0 Enki:~/Code/node(master) $
  93. 93. Yay. Easy.
  94. 94. Install instructions https://github.com/isaacs/npm
  95. 95. Express.js
  96. 96. Sinatra Style MVC framework
  97. 97. var app = require('express').createServer(); app.get('/', function(req, res){ res.send('hello world'); }); app.listen(3000);
  98. 98. HTTP Verb Oriented
  99. 99. Middleware
  100. 100. app.use(express.bodyParser()); app.use(express.cookieParser()); app.post('/', function(req, res){ // Perhaps we posted several items with a form // (use the bodyParser() middleware for this) var items = req.body.items; console.log(items); res.send('logging'); });
  101. 101. Templating
  102. 102. var express = require("express"); app.configure(function () { var public = __dirname + "/../public/"; public = require("path").normalize(public); app.set("views", __dirname + "/views"); app.set("view engine", "jade"); }); app.get("/", function (req, res) { res.render("index", { locals : { h1: 'Router Stats', scripts : [ "/public/smoothie.js", "/public/raphael.js", "/public/base.js", "/public/gfx.js", "/public/explore.js", "/public/app.js" ] } } });
  103. 103. Exercise • Create an Express server • Serve two different pages based on value of the HTTP Get param "page" • Create a redirect from /old to /new • Set a cookie on the client
  104. 104. Express in depth
  105. 105. Routes • Routes are based on verbs • GET • POST • PUT • DELETE • ALL (not a real verb, but obvious)
  106. 106. Simple routes
  107. 107. app.get(‘/’, function(req,res) { res.send(‘hello root’); });
  108. 108. Routes with variables
  109. 109. app.get(‘/user/:id’, function(req,res) { res.send(‘hello ‘ + req.params.id); });
  110. 110. Optional flags in routes
  111. 111. app.get(‘/:filename?’, function(req,res) { if(req.params.filename) { res.send(req.params.filename); } else { res.send(‘root’); } });
  112. 112. Regex as routes
  113. 113. app.get(///, function(req, res) { //like using ‘/’ ? res.send(‘/’); });
  114. 114. app.get(/^/d+/?$/, function(req,res) { res.send(‘matches a number’); });
  115. 115. app.get(/^/(.+)/?$/, function(req,res) { //note translation of %23, etc res.send(‘Got: ’ + req.params[0]); //also captures become an array });
  116. 116. Using regex to define parameters
  117. 117. app.get(‘/index.:format((html|json))’, function(req,res) { res.send('Got: ' + req.params.format); });
  118. 118. app.get('/:id(d+)', function(req,res) { //only digits, right? res.send(req.params.id); });
  119. 119. app.get('/:id(d+)', function(req,res) { //escape your in strings res.send(req.params.id); });
  120. 120. Routing magic: Router.js function normalizePath(path, keys) { path = path .concat('/?') .replace(//(/g, '(?:/') .replace(/(/)?(.)?:(w+)(?:((.*?)))?(?)?/g, function(_, slash, format, key, capture, optional){ keys.push(key); slash = slash || ''; return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || '([^/]+?)') + ')' + (optional || ''); }) .replace(/([/.])/g, '$1') .replace(/*/g, '(.+)'); return new RegExp('^' + path + '$', 'i'); }
  121. 121. Routing magic • If . before :variable? then . is also optional • If ? is not after a variable then only the previous character is affected • / at the end of URLs automatically optional • * Can be used as a wildcard in routes • Includes when end of URL is optional e.g. '/app/e?' • Regex can be used any place in a route string e.g. '/app/(d)r?'
  122. 122. Exercises • Create an express app with routes that capture '/' '/products' and '/services' • Create a route that captures the product ID after '/product/' e.g. '/product/abc12' and returns it in the response • Use a regular expression to restrict the ID parameter to 3 letter followed by 3-5 numbers • Create a route using a regex that matches the entire route
  123. 123. Passing Control • Routes are actually 'stacked middleware' • You can pass control between routes • The next() function calls the next matching route
  124. 124. app.get('/users/:id', function(req, res, next){ var id = req.params.id; if (checkPermission(id)) { // show private page } else { next(); } }); app.get('/users/:id', function(req, res){ // show public user page });
  125. 125. Passing Control • next() is a function of router (and defined in the closure containing the route) • router will grab routes in the order they were declared • e.g. since'/*' will match everything so it should be the last route! • router doesn't care about verbs so you can use all() to operate on all verbs/routes and then use next() to pass to get(), put(), etc
  126. 126. Exercises • Create a simple check for correct product IDs if not pass control to a route showing a custom error page • Use app.all() to check user permission before showing (mock-up) edit controls on a web site
  127. 127. Middleware
  128. 128. It's a pattern
  129. 129. req, res next() next() next()
  130. 130. req, res next() next() Dispatcher next() req res
  131. 131. req, res, next
  132. 132. var express = require('express'), app = express.createServer(); var middleware = function (req, res, next) { req.foo = 'bar'; next(); }; app.use(middleware); app.get('/', function(req, res) { res.send(req.foo);
  133. 133. var express = require('express'), app = express.createServer(); var middleware = function (req, res, next) { var send = res.send; res.send = function(d) { res.send = send; res.send('hijacked! ' + d); } next(); }; app.use(middleware); app.get('/', function(req, res) { res.send('hi'); });
  134. 134. Connect middleware (Renamed express.* for convenience) • logger • profiler • bodyParser • responseTime • cookieParser • basicAuth • session • favicon • static • vhost • errorHandler
  135. 135. var express = require('express'), app = express.createServer(); app.use(express.logger()); app.use(express.bodyParser()); app.use(express.cookieParser()); app.use(app.router); app.use(express.static(__dirname + '/images')); app.use(express.errorHandler()); app.get('/', function(req, res) { res.send('<html><img src="/image.png"></html'); }); app.listen(9003);
  136. 136. Ordering matters
  137. 137. Router uses "internal middleware"
  138. 138. var express = require('express'), app = express.createServer(); var middleware = function (req, res, next) { req.foo = 'bar'; next(); }; app.get('/', middleware, function(req, res) { res.send(req.foo); });
  139. 139. var a, b, c, d; a = b = c = d = function(req,res,next) { next(); } var set1 = [a,b]; var set2 = [c,d]; var all = [set1, set2]; app.get('/set1', set1, function(req,res) { res.send('output'); }); app.get('/set2', [c,d], function(req,res) { res.send('output'); }); app.get('/all', all, function(req,res) { res.send('output'); });
  140. 140. Middleware factories
  141. 141. Middleware are just functions
  142. 142. var a, b, c, d; a = b = c = d = function(req,res,next) { next(); } var set1 = [a,b]; var set2 = [c,d]; var all = [set1, set2]; app.get('/set1', set1, function(req,res) { res.send('output'); }); app.get('/set2', [c,d], function(req,res) { res.send('output'); }); app.get('/all', all, function(req,res) { res.send('output'); });
  143. 143. var mFactory = function(letter) { return function(req,res,next) { var send = res.send; res.send = function(d) { res.send = send; res.send(letter + ' ' + d); } next(); } }; var set1 = [mFactory('a'),mFactory('b')]; var set2 = [mFactory('c'),mFactory('d')]; var all = [set1, set2]; app.get('/set1', set1, function(req,res) { res.send('output'); }); app.get('/set2', set2, function(req,res) { res.send('output'); }); app.get('/all', all, function(req,res) { res.send('output'); });
  144. 144. Exercise • Create a middleware to detect mobile phone browsers and attach a boolean to req • Create an express app that serves up links to an image using staticProvider • Modify Profiler to profile your app and write each profile to a log file • Create a middleware factory that sets the HTTP Expires header based on roles
  145. 145. Error handling
  146. 146. function NotFound(msg){ this.name = 'NotFound'; Error.call(this, msg); Error.captureStackTrace(this, arguments.callee); } NotFound.prototype.__proto__ = Error.prototype; app.get('/404', function(req, res){ throw new NotFound; }); app.get('/500', function(req, res){ throw new Error('keyboard cat!'); });
  147. 147. app.error(function(err, req, res, next){ if (err instanceof NotFound) { res.render('404.jade'); } else { next(err); } });
  148. 148. View Rendering
  149. 149. app.get('/', function(req, res){ res.render('index.ejs', { title: 'Falsy Demo' }); });
  150. 150. Enki:~/Code/express-demo $ tree . app.js lib public views index.ejs layout.ejs layout1.ejs partials stylesheet.ejs 4 directories, 5 files Enki:~/Code/express-demo $
  151. 151. Don't forget to install npm install ejs npm install jade
  152. 152. layout.ejs <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title><%= title %></title> </head> <body> <%- body %> </body> </html>
  153. 153. • layout is a framework unless you turn it off • body is a special variable for layout referring to the file specified
  154. 154. app.set('view engine', 'ejs'); app.get('/', function(req,res) { res.render('index', { title:'Falsy Demo'}); });
  155. 155. //global app.set('view options'), { layout: false; }); //or per route res.render(index, {layout: false});
  156. 156. Partial views
  157. 157. View Partials • Repeating elements • Take a collection • Iterate over the collection • "Built in" variables for managing collections
  158. 158. layout.ejs <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <%- partial('stylesheet', stylesheets) %> <title><%= title %></title> </head> <body> <h1><%= header %></h1> <%- body %> </body> </html>
  159. 159. partials/stylesheet.ejs <link rel="stylesheet" type="text/css" href="<%- stylesheet %>">
  160. 160. res.render('index', { locals: {'title': title, 'header': header, 'content': content, stylesheets: ['/public/style.css'] }, } );
  161. 161. Exercises • Create an express server that use jade, haml, ejs to render an index page • Create a public folder and include file from it (CSS, images, etc) in your layout • Create a route for '/blog/id' to accept only digits • Make a 'fake database' (array) of blog posts and use a middleware to validate each id is valid • Create a view for the '/blog/id' show the correct post • Use a view partial to a preview of each blog post on the index page
  162. 162. Questions

Editor's Notes

  • \n
  • Running order\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Service behind traffic server\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

×