OSCON 2011 - Node.js Tutorial

15,356 views
15,192 views

Published on

Published in: Technology
2 Comments
55 Likes
Statistics
Notes
No Downloads
Views
Total views
15,356
On SlideShare
0
From Embeds
0
Number of Embeds
670
Actions
Shares
0
Downloads
666
Comments
2
Likes
55
Embeds 0
No embeds

No notes for slide
  • \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
  • OSCON 2011 - Node.js Tutorial

    1. 1. Node.js WorkshopTom Hughes-CroucherChief Evangelist / Node Tech Lead@sh1mmertom@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 JavaScriptNode Up and Running Tom Hughes-Croucher
    5. 5. Major update this week
    6. 6. Why Server-Side JavaScript?
    7. 7. JavaScriptprogrammers 3>2>1
    8. 8. Massive Code base of jQuery and other JS libraries
    9. 9. Laziness or “I’m sickof writing stuff twice”I could have said efficiency, but I think we allsecretly long to sit around in our underwear.
    10. 10. ProgressiveEnhancement 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 awesomewhy is it "new"?
    13. 13. 1. Professionalism
    14. 14. “Yahoo!s corporate motto is: Dont be eval().“
    15. 15. 2. JavaScript Runtimes
    16. 16. Runtimes• V8 (Google), C++• Spider Monkey (Mozilla), C++• Rhino (Mozilla), Java
    17. 17. V8 Spider MonkeyJavaScript Performance
    18. 18. 8xSep 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. Its JavaScript
    28. 28. See Above.
    29. 29. 2. Its Fast
    30. 30. concurrency=300, Smaller is Better 400 300response time (ms) server nginx 200 thin tornado node_buffer 100 24 26 28 210 212 214 216 218 response size (bytes)
    31. 31. 3. Its easy to extend
    32. 32. Modules in JSAdd-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 Nodecommunity 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. Navea.k.a the easy way
    42. 42. Enki:~ $ wget -q http://github.com/isaacs/nave/raw/master/nave.shEnki:~ $ chmod 755 nave.shEnki:~ $ ./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/#downloadand get the URL of the current stable release
    46. 46. Enki:~ $ wget -q http://nodejs.org/dist/node-v0.4.10.tar.gzEnki:~ $ tar xzf node-v0.4.10.tar.gzEnki:~ $ cd node-v0.4.10Enki:~/node-v0.4.10 $
    47. 47. Local or system?
    48. 48. Local
    49. 49. Enki:~/node-v0.4.10 $ mkdir ~/localEnki:~/node-v0.4.10 $ ./configure --prefix=~/localChecking for program g++ or c++ : /usr/bin/g++Checking for program cpp : /usr/bin/cpp...Checking for fdatasync(2) with c++ : noconfigure finished successfully (3.466s)
    50. 50. Enki:~/node-v0.4.10 $ makeWaf: Entering directory `/Users/sh1mmer/node-v0.4.10/buildDEST_OS: darwinDEST_CPU: x86Parallel 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 installWaf: Entering directory `/Users/sh1mmer/node-v0.4.10/buildDEST_OS: darwinDEST_CPU: x86Parallel 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.hWaf: Leaving directory `/Users/sh1mmer/node-v0.4.10/buildinstall 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/binEnki:~/node-v0.4.10 $ node -v-bash: node: command not foundEnki:~/node-v0.4.10 $ echo PATH=~/local/bin:$PATH>> ~/.profileEnki:~/node-v0.4.10 $ node -vv0.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 -vv0.4.7Enki:~/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-replInteractive JavaScript terminal
    58. 58. $Enki:~ $ node
    59. 59. $Enki:~ $ node>3>2>1false> true == 1true> true === 1false
    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> .clearClearing context...> .exitEnki:~ $
    61. 61. Enki:~ $ node> var foo = "bar";> foo;bar> .clearClearing context...> fooReferenceError: 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 --debughelloworld.jsdebugger listening on port 5858Server running at http://127.0.0.1:8124/
    69. 69. Enki:~ $ npm install node-inspectornode-inspector@0.1.8 ./node_modules/node-inspector websocket-server@1.4.04 paperboy@0.0.2Enki:~ $ node-inspectorvisit http://0.0.0.0:8080/debug?port=5858 to startdebugging
    70. 70. Exercises• Modify the HTTP server to return the text "Im 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) DestinationRequest 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 functionsmyObj.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.jsexports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum;};increment.jsvar 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. Protipexports.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 PackageManager (NPM)
    90. 90. NPM is written inJavaScript for Node
    91. 91. Enki:~ $ cat `which npm`#!/usr/bin/env node;(function () { // wrapper in case were in module_context modevar 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 expressexpress@2.3.11 ../../node_modules/express mime@1.2.2 connect@1.4.2 qs@0.1.0Enki:~/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 todefine 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.jsfunction 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 doesnt 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. Its a pattern
    129. 129. req, resnext()next()next()
    130. 130. req, resnext()next() Dispatchernext() 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 "internalmiddleware"
    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.ejs4 directories, 5 filesEnki:~/Code/express-demo $
    151. 151. Dont forget to installnpm install ejsnpm 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. //globalapp.set(view options), { layout: false;});//or per routeres.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

    ×