Node.js workshop



Node.js workshop

0 1


nodejs-workshop

This is the slides from a workshop I'm running on Node.js (don't worry, there's lots more to the talk than is in these slides!)

On Github benjie / nodejs-workshop

Node.js workshop

By @Benjie

Introduction

Installing Node.js

Get the latest stable version of Node from nodejs.org/download and install it.

Mac: brew install node

Linux: [sudo] tar zxf node-*.tar.gz --strip-components=1 -C /usr/local

$ uname -a # Linux .... i686 < 32bit or something64 < 64bit

Once installed, run node in the command line:

$ node
> 1+2
3
> ^D

You may need to update your $PATH so it can find node

What is node?
  • Not a networked web browser - No DOM*!
  • Single threaded
  • Good at I/O bound
  • Not good at computation heavy (at least not in a server setting

Hello World!

Create a folder somewhere for this workshop.

cd; mkdir node-workshop; cd !$

Add the following to a new file hello-world.js

console.log("Hello world!");

And then run it:

$ node hello-world.js
Hello world!
$ 

Hello REPL!

Read-Eval-Print-Loop

$ node
> console.log("Hello world!");
Hello world!
undefined
> 1+2
3
> {}+{}
'[object Object][object Object]'
> 

JavaScript Basics

Super Quick Summary

/* Variables  */ var one = 1;
/* Objects    */ var obj = {}; obj.property = one;
/* Arrays     */ var arr = [one, obj];
/* Primitives */ var prims = [-1.3, "Str", true, null, undefined]; 
/* Buffers    */ var b = new Buffer("Hello world!");
                 b.toString('base64'); /* or hex or utf8 or ... */
/* Undefined  */ var v; if (v === undefined) { console.log("Careful"); } 
/* typeof     */ if (typeof v === 'undefined') { console.log("Good"); } 
/* Accessing  */ console.log(arr[1]['property']);
/* Concat     */ console.log("Join " + val + " and " + 3.03);                    // Fix code colouring 
/* Functions  */ function myFunc(param) { console.log("Got: "+param); }
/* 1st class  */ function seven(fn) { fn(7); }; seven(myFunc);
/* Anonymous  */ seven(function(nr) { console.log(nr * nr); }); 

Classes

function Animal() {}
var a = new Animal();
Animal.prototype.getType = function() {
  return 'an animal';
}
Animal.prototype.announceSelf = function() {
  console.log("I am "+this.getType());
}
a.announceSelf(); // I am an animal

Classes

function Cat() {}
Cat.prototype = new Animal();
Cat.prototype.getType = function() {
  return 'a cat';
}
var c = new Cat();
c.announceSelf(); // I am a cat
var c2 = new Cat();
c2.getType = function(){ return 'a beautiful cat'; }
c2.announceSelf(); // I am a beautiful cat
var c3po = new Cat();
c3po.getType = function(){ return 'a robotic cat'; }
c3po.announceSelf(); // I am a robotic cat
c.announceSelf(); // I am a cat
a.announceSelf(); // I am an animal

Node.js - runloop

Source: http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/

Here are the options we have for dealing with this:

  • Synchronous (procedural)
  • Fork
  • Threads
  • Events (callbacks)
Synchronous: easy; slow, queues Fork: easy; doesn't scale. Threads: easy, nicer than fork; complicated access to shared resources.

Threads vs events

A programmer had a problem. He thought I know, I'll solve it with threads! . has Now problems. two he

— azeem (@azeem) June 4, 2014

Source: http://blog.webfaction.com/2008/12/a-little-holiday-present-10000-reqssec-with-nginx-2/

Single thread for JavaScript

"Pens down!"

Your code should quickly request work, optional callback.

var http = require('http');
function listener(request, response) {/* ... */}
var server = http.createServer(listener /* <- callback */);
server.listen(1337); /* <- request work */

Background thread pool handles work.

GET / HTTP/1.0

Node triggers callback when IO occurs/completes.

var request = new http.IncomingMessage(/* ... */);
var response = new http.ServerResponse(/* ... */);
listener(request, response);

Repeat

function listener(request, response) {
  response.end("Hello world!"); /* <- request work */
} 

npm

Node Package Manager

npm - node package manager

Comes with Node.js nowadays

Search: npm search graceful-fs

Install: npm install graceful-fs coffee-script

Install globally: [sudo] npm install -g nodemon

Installs to node_modules

Include: var fs = require('graceful-fs');

Uninstall: npm uninstall graceful-fs

npm tips

Create a package.json interactively: npm init

Prevent accidental publishing to npm: "private":true

Install and save dependency: npm install --save express

Save by default: npm config set save true

Jump to documentation for a module: npm docs coffee-script

more npm tips

Define scripts:

"scripts": {
  "test": "echo All tests pass",
  "truth": "echo Node.js rocks",
  "start": "node index.js"
}

Run tests: npm test

Run other scripts: npm run truth

Start the main app: npm start

Node.js - webserver

Hello Internet!

Enter the following into server.js

var http = require('http');

function listener(request, response) {
  response.end("Hello world!");
}

var server = http.createServer(listener);
server.listen(1337);

Then run it:

$ node server.js

Open http://localhost:1337

Exhibitionist

Let's share our own source code with the world:

var http = require('http');
var fs = require('fs');

function listener(request, response) {
  function send(err, contents) {
    if (err) {
      console.error(err); // DON'T DO THIS IN PRODUCTION
      response.writeHead(500);
      response.end("ERROR");
    } else {
      response.end(contents);
    }
  }
  fs.readFile(require.main.filename, send);
}

var server = http.createServer(listener);
server.listen(1337);

But this means if we have a traffic surge we'll get EMFILE - too many files open.

Graceful Exhibitionist

Let's gracefully share our own source code with the world:

var http = require('http');
//var fs = require('fs');
var fs = require('graceful-fs'); // npm install graceful-fs

function listener(request, response) {
  function send(err, contents) {
    if (err) {
      console.error(err); // DON'T DO THIS IN PRODUCTION
      response.writeHead(500);
      response.end("ERROR");
    } else {
      response.end(contents);
    }
  }
  fs.readFile(require.main.filename, send);
}

var server = http.createServer(listener);
server.listen(1337);

But this means we have to open the file over and over again, despite it's relatively small size

Preloading

Save server.js as server.preloading.js

var http = require('http');
var fs = require('graceful-fs');
var fileContents;

function listener(request, response) {
  response.end(fileContents);
}

var server = http.createServer(listener);

function read(err, contents) {
  if (err) {
    console.error(err);
    process.exit(1);
  } else {
    fileContents = contents;
    server.listen(1337);
  }
}

fs.readFile(require.main.filename, read);

Debugging

node-inspector

npm install -g node-inspector
node --debug server.js
node-inspector &

http://127.0.0.1:8080/debug?port=5858 in Chrome

Select server.js on the left

Add a breakpoint to response.send(contents);

Load your server (http://localhost:1337/)

Now edit the code!

Modules

Module basics

Own closure

exports

module.exports

/* server.js */
var http = require('http');
var myModule = require('./my-module');

var server = http.createServer(
  myModule.serveSource
);
server.listen(1337);
/* my-module.js */
var fs = require('graceful-fs');

function listener(request, response) {
  function send(err, contents) {
    if (err) {
      console.error(err);
      response.writeHead(500);
      response.end("ERROR");
    } else {
      response.end(contents);
    }
  }
  fs.readFile(
    require.main.filename,
    send
  );
}

exports.serveSource = listener;

Express

CoffeeScript

Callback hell

callbackhell.com