NodeJs

Quoting the Node.js website

"Node.js(r) is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices."

See also Wikipedia node.js, here is the Node.js API. I also like this book (German only) Node.js Das umfassende Handbuch (German).

Node.js getting started

You may start the Node.js interactive mode by running node.exe. It is called REPL.

# node.exe
> console.log('Test');
Test
undefined

Ignore the word undefined in the output it is just there to show you the return value of the console.log function, which is not set. It will appear everywhere in the interactive mode, but I will skip it from now on.

Assign a value to a variable

> var x='Hello World';
> console.log(x);
Hello World

Interactive mode commands

.break
.clear
.exit
.help
.load
.save

Normally you start your code like this:

# node mycode.js

Node.js IDE

The easiest way to get started right away is probably to get an account at Cloud9 and develop within your browser on their cloud IDE. There is also the Eclipse plugin Nodeclipse for Node.js. Another way seems to be to install it via npm (part of the Node.js installation), but that did not bring it into my Eclipse (is it supposed to?)

# npm install -g nodeclipse
# npm install -g express-generator
# npm install -g express

Once it works you can add in Eclipse a new Node.js Project (via New Project).

Modules

Node.js comes with a lot of modules you use for your project. You can load them with the require statement and install new ones with the npm command. Often used Node.js modules

Write your own module

Place this into a file called MyFirstModule.js

exports.sayHello = function(x) {
    return 'Hello ' + x;
};

Now once you require this file, it will load your module and make everything with exports available

var foo = require('./MyFirstModule.js');
console.log(foo.sayHello('World'));

Within your module you can get some information about it

console.log(module.id);
console.log(module.filename);
console.log(module.loaded);
console.log(module.parent);
console.log(require.main.filename);

Node.js will search your module first in the folder node_modules in the current directory, then in the directory above the current directory, etc., then in the folders listed in the NODE_PATH variable and then in your home directory.

If you require a folder and not a file, the file package.json is searched there.

Automated restarts on code changes

Your program has to be stopped and restarted every time you can something in the code. Let this happen automatically

# npm i nodemon -g
# nodemon myFirstNodejsCode.js

Unit testing

var assert = require('assert');
assert.ok(42 < 45);
assert.ok(42 < 5);
console.log('Done');

Events

Node.js works a lot with callback functions being called, triggered by events. Normally you just provide the callback function and the events are handled by the module you are dealing with, but you can also do it yourself

var callback1 = function() {
    console.log('dring');
}

var callback2 = function() {
    console.log('dingdingding');
}

var events = require('events');
var ee=new events.EventEmitter();
ee.once('ringTheBell', callback1);
ee.on  ('ringTheBell', callback2);

ee.emit('ringTheBell');
ee.emit('ringTheBell');

ee.removeListener('ringTheBell', callback2);

ee.emit('ringTheBell');

Output:

dring
dingdingding
dingdingding

Timers

var myStopWatchCallBackFunction = function(s, n) {
    console.log('s: ' + s);
    console.log('n: ' + n);
}

var myStopItFunction = function(inter) {
    // stop a running timeout
    clearTimeout(inter);
}

// start this every 3 seconds
var interval=setInterval(myStopWatchCallBackFunction, 3000, 'Hello World', 42);

// start this once after 10 seconds
var timeout =setTimeout(myStopItFunction,            10000, interval);
 

Utility

Helper method, logging, String formating, type testing, show object details with inspect, inheriting via

util.inherits(Child, Parent);

Logger

bunyan

Callback hell

async

IO

Console

console.time('myStopWatch');
console.log('Hello World');
console.timeEnd('myStopWatch');
Hello World
myStopWatch: 2ms

Path

You should use path and path.join or path.sep to build a path to a file to respect the separator on the system Node.js is running.

Reading and writing Files

This uses the fs module to read a file. Once the data has been read it is passed to the function you provided. Please note that it is very likely that the first output will be the word Done before the actual file content is outputted, because the fs module will need some time to read the file.

var fs = require('fs');

var myFile = "c:temptg.txt";

// overwrite the file with a new line
fs.writeFile(myFile, "Helllo world", function (err, data) {
                if(err) {throw err};
                // if we reach here, file has been overwritten, append some data now
                fs.appendFile(myFile, " and hello Moon", function(err, data) {
                    if(err) {throw err;}
                    // if we reach here, file has an extra line, now read the whole file again
                    fs.readFile(myFile, function (err, content) {
                        if(err) {throw err;}
                        console.log(content.toString());
                    fs.close();
                    });
        });
});
console.log('Done');

Streams

Streams are used in several parts of Node.js.

var fs=require('fs');

// the stream we read from (a file)
var myReadStream=fs.createReadStream('c:temptg.txt' ); // fs.createReadStream('c:temptg.txt', { encoding: 'utf-8' } );

// the stream we write to
var myWriteStream=fs.createWriteStream('c:temptg2.txt');

// the stream we write to (standard output)
//var myWriteStream=process.stdout;

// file opened
myReadStream.on("open", function(fd) {
       console.log('Event Open');
       // if you set this the data event will get a human readable string
       //myReadStream.setEncoding('utf-8');
});

// data comes in
myReadStream.on("data", function(data) {
       console.log('Event Data!');
       // if the receiver is busy you might be nice and pause sending more data
       if(!myWriteStream.write(data)) {
              console.log('Write stream is full');
              myReadStream.pause();
       }
});

// the receiver is ready to get more data
myWriteStream.on("drain", function() {
       console.log("Write stream is no longer full");
       myReadStream.resume();
});

// stream closed
myReadStream.on("end", function() {
       console.log("End!");
});

console.log('Done.');

Instead of manually copying the data from one stream into the next one you may also use a pipe to connect them

//connect the readable stream with the writeable stream
myReadStream.pipe(myWriteStream);

Streams2

Why streams2 is better than streams.

Network

Webserver and Client

Being able to program a webserver and webclient with only few lines of code is one of the strengths of Node.js

var http = require('http');
http.createServer().listen(8123, '127.0.0.1');
console.log('Now connect to http://127.0.0.1:8123');

This is already a running webserver, even though it does not return any data so a client will wait forever. Let's change this. We pass a function to the createServer method, which will be started whenever somebody connects to our server with a request. The function gets the request and the response which will be send. We change the response to really return something

var http = require('http');
http.createServer(function (request, response) {
                response.writeHeader(200, {'content-type': 'text/plain; charset=utf-8' });
                response.write('Hello ');
                response.write('everybody ');
                response.end(' out there!');
}).listen(8123, '127.0.0.1');
console.log('Now connect to http://127.0.0.1:8123');

The same thing but showing more what it is happening in the background

var http = require('http');
var server=new http.Server();

server.addListener('request', function(request, response) {
       response.statusCode=200;
       response.setHeader('content-type', 'text/plain');
       response.end('Hello everybody out there!');
});

server.listen(8123, '127.0.0.1');
console.log('Now connect to http://127.0.0.1:8123');

Please note that your function is only called in the event of somebody connects to the server with a request. Otherwise your code just does nothing. This is a main principal in Node.js. Do define what function to call in an event and the rest just happens.

If your client is a browser, sending HTML is nicer

var http = require('http');
http.createServer(function (request, response) {
                response.writeHeader(200, {'content-type': 'text/html; charset=utf-8' });
                var myBody='<html>' +
                    '<head><title>Hello World</title></head>' +
                    '<body>' +
                    '<h1>Greeting</h1>' +
                    'Hello World' +
                    '</body>' +
                    '</html>';
                response.end(myBody); // if you only have one response you can pass it to the end method, no need for write
}).listen(8123, '127.0.0.1');
console.log('Now connect to http://127.0.0.1:8123');

Request

What is coming from the client to you. For example during a file upload this might be big und split into different chunks.

var http = require('http');
http.createServer(function (request, response) {

       // collect all the requests
       var reqData="";

       // new request
       request.on('data', function(data) {
              reqData=reqData+data.toString();
       });

       // we got them all, now return it
       request.on('end', function() {
           response.writeHeader(200, {'content-type': 'text/plain; charset=utf-8' });
           response.write('We got this request: ');
           response.write(reqData);
           response.end();
       });

}).listen(8123, '127.0.0.1');
console.log('Now connect to http://127.0.0.1:8123');

HTTP Client

Do no longer use

createClient

it is deprecated!

Simple example which also checks for http client request exceptions

var http = require('http');

// build the request
var myRequest=http.request('http://www.tgunkel.de/index.de.php', function(response) {
       var myServerData="";

       response.on('data', function(data) {
              console.log('Got new data from server: ');
              myServerData=myServerData+data;
       })

       response.on('end', function () {
              console.log("This is the result");
              console.log(myServerData);
       });
});

// catch http client request exceptions, e.g. getaddrinfo ENOTFOUND
myRequest.on('error', function(err) {
       console.log("We did hit an error" + err);
});

myRequest.end(); // this will actually send the request to the server

console.log('Done.');

You can also provide more options than just the URL

var myOptions={
              host:   'www.tgunkel.de',
              port:   '80',
              method: 'GET',
              path:   '/index.de.php',
              headers: { 'accept': 'text/html' }
       }
var myRequest=http.request(myOptions, function(response) {
...
});

This way you can even use an http proxy:

var myOptions={
              host:   'myproxy.example.com',  // proxy host
              port:   '8080',                        // proxy port
              method: 'GET',
              path:   'http://www.tgunkel.de/index.de.php', // full path here
              headers: { Host: 'www.tgunkel.de' }           // repeat the true hostname you want to go to again here
       }

// build the request
var myRequest=http.request(myOptions, function(response) {
      ...

Shorter version for GET requests which also does not need a call to request.end()

var myRequest=http.get(myOptions, function(response) {
       response.on('data', function(data) {
              console.log('Got new data from server: ');
              console.log(data.toString());
       })
});

You can also get only the request object and add an response callback method afterwards.

var myOptions='http://www.tgunkel.de';

var myRequest=http.request(myOptions);

myRequest.on('response', function(response) {

       // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
       switch(response.statusCode) {
              case 200:
                     // content type until the first ;
                     myContentType=response.headers['content-type'].split(';');
                     myContentType=myContentType[0];

                     // content type
                     switch(myContentType) {
                           case 'text/html':
                                  response.on('data', function(data) {
                                         console.log('HTML data: ', data.toString());
                                  });
                                  break;
                           case 'text/plain':
                                  response.on('data', function(data) {
                                         console.log('TEXT data: ', data);
                                  });
                                  break;
                           default:
                                  // do something about the unexpected content type!
                                  console.log('Unexpected content type: ' + response.headers['content-type']);
                     }
                     break;
              default:
                     // do something about the unexpected status code!
                     console.log('Unexpected status code: ' + response.statusCode)
                     break;
       }
});

myRequest.end(); // this will actually send the request to the server

URL

var url=require('url');
var querystr=require('querystring');

myUrlTxt='http://www.tgunkel.de:80/index.php?query=do+it&uml;foo=bar';

var u=url.parse(myUrlTxt);

console.log(querystr.parse(u.query));  // { query: 'do it', foo: 'bar' }

console.log(u);
/*
{ protocol: 'http:',
slashes: true,
auth: null,
host: 'www.tgunkel.de:80',
port: '80',
hostname: 'www.tgunkel.de',
hash: null,
search: '?query=do+it&uml;foo=bar',
query: 'query=do+it&uml;foo=bar',
pathname: '/index.php',
path: '/index.php?query=do+it&uml;foo=bar',
href: 'http://www.tgunkel.de:80/index.php?query=do+it&uml;foo=bar' }
*/


// build url

var x = {
       protocol: 'http',
       hostname: 'www.tgunkel.de',
       port: '80',
       pathname: 'foo/bar',
       query: { search: 'bird' }
};

url.format(x);  // http://www.tgunkel.de:80/foo/bar?search=bird

TCP

http://www.hacksparrow.com/tcp-socket-programming-in-node-js.html

The TCP server

var net=require('net')

// bind to TCP port
server=net.createServer(function(sock) {

       // we got a client
       console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort);

       // send a message to the client
       sock.write('Server speaking');

       // we got data from a client
       sock.on('data', function(data) {
              console.log("Server got this message: "+data);

       });

       // client goes away
       sock.on('close', function(data) {
              console.log('CLOSED: ');
       });

}).listen(7777);

server.on('end', function() {
    console.log('Client disconnected');
});

//we got a problem
server.on('error', function(err) {
       console.log('Server got error'+err.toString());
});

The TCP client

var net=require('net')

// connect to TCP port
client=net.connect({ port: 7777 }, function() {
       console.log('Client connected to server');
       client.write('Client says hello to server');
});

client.setEncoding('utf8');

// client got data from server
client.on('data', function(data) {
       console.log('Client got data: '+data);

       client.write('Hey, server got your message!');
       client.write('GOODBYE');
       client.end();
});

// error
client.on('error', function(err) {
       console.log('Client error: '+err.toString());
});