Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

OSCON 2011 - Node.js Tutorial

15,955 views

Published on

Published in: Technology

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

×