SlideShare a Scribd company logo
@crichardson
NodeJS: the good parts?
A skeptic’s view
Chris Richardson
Author of POJOs in Action
Founder of the original CloudFoundry.com
@crichardson
chris.richardson@springsource.com
http://plainoldobjects.com
@crichardson
Presentation goal
How a curmudgeonly
server-side Java developer
discovered an appreciation
for NodeJS and JavaScript
@crichardson
About Chris
@crichardson
(About Chris)
@crichardson
About Chris()
@crichardson
About Chris
@crichardson
About Chris
http://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/
@crichardson
vmc push About-Chris
Developer Advocate
Signup at http://cloudfoundry.com
@crichardson
Agenda
Overview of NodeJS
Building a front-end server with NodeJS
Taming tangled asynchronous code with promises
@crichardson
What’s NodeJS?
Designed for DIRTy apps
@crichardson
Small but growing rapidly
Busy!
@crichardson
NodeJS Hello World
app.js
$ node app.js
$ curl http://localhost:1337
http://nodejs.org/
Load a module
request handler
@crichardson
NodeJS
JavaScript
Reactor
pattern
Modules
@crichardson
NodeJS
JavaScript
Reactor
pattern
Modules
@crichardson
Dynamic, weakly-typed
Dynamic:
Types are associated with values - not variables
Define new program elements at runtime
Weakly typed:
Leave out arguments to methods
Access non-existent object properties
Weird implicit conversions: 99 == “99”!
truthy and falsy values
Comprehensive tests are essential
@crichardson
JavaScript is object-oriented
> var fred = {name: “Fred”, gender: “Male”};
undefined
> fred.name
“Fred”
> console.log("reading age=" + fred.age);
reading age=undefined
undefined
> fred.age = 99;
99
> fred
{ name: 'Fred',
gender: 'Male',
age: 99 }
> delete fred.age
true
> fred
{ name: 'Fred', gender: 'Male' }
Unordered key-value pairs
Keys = properties
Add property
Delete property
@crichardson
JavaScript “classes”
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () { console.log("Hello " + this.name); };
var chris = new Person("Chris");
chris.sayHello();
Looks like a
constructor?!?
What’s that?!?!
This Java-like syntax is a mess
because JavaScript isn’t class
based
Looks familiar
@crichardson
overrides
JavaScript is a prototypal
language
__proto__
a 99
__proto__
a 100
b 200
inherited
Prototype
X
Y
@crichardson
Prototypal code
$ node
> var person = {};
undefined
> person.sayHello = function () { console.log("Hello " + this.name); };
[Function]
> var chris = Object.create(person, {name: {value: "Chris"}});
undefined
> var sarah = Object.create(person, {name: {value: "Sarah"}});
undefined
> chris.sayHello();
Hello Chris
undefined
> sarah.sayHello();
Hello Sarah
undefined
> chris.sayHello = function () { console.log("Hello mate: " + this.name); };
[Function]
> chris.sayHello();
Hello mate: Chris
undefined
Not defined
here
prototype properties
@crichardson
JavaScript is Functional
function makeGenerator(nextFunction) {
var value = 0;
return function() {
var current = value;
value = nextFunction(value);
return current;
};
}
var inc = makeGenerator(function (x) {return x + 1; });
> inc()
0
> inc()
1
Pass function as an
argument
Return a function
closure
@crichardson
Partial function application
> var join = require("path").join;
undefined
> join("/x", "y")
'/x/y'
> var withinx = join.bind(undefined, "/x");
undefined
> withinx("y");
'/x/y'
>
partially apply join
@crichardson
Created in a hurry with the
goal of looking like Java
The ‘Java...’ name creates expectations that it can’t satisfy
Fake classes: Hides prototypes BUT still seems weird
global namespace
scope of vars is confusing
Missing return statement = confusion
‘function’ is really verbose
‘this’ is dynamically scoped
@crichardson
JavaScript is the only way to
get things done in the
browser
@crichardson
Stockholm syndrome
“Stockholm syndrome ... is a psychological
phenomenon in which hostages ... have
positive feelings toward their captors,
sometimes to the point of defending them...”
http://en.wikipedia.org/wiki/Stockholm_syndrome
@crichardson
Martin Fowler once said:
"...I'm one of those who despairs that a
language with such deep flaws plays such an
important role in computation. Still the
consequence of this is that we must take
javascript seriously as a first-class language
and concentrate on how to limit the damage
its flaws cause. ...."
http://martinfowler.com/bliki/gotoAarhus2012.html
@crichardson
Use just the good parts
@crichardson
Use a better language that
compiles to JavaScript
TypeScript
Typed parameters and fields
Classes and interfaces (dynamic structural typing)
Dart
Class-based OO
Optional static typing
Bidirectional binding with DOM elements
@crichardson
CoffeeScript Hello World
http = require('http')
class HttpRequestHandler
constructor: (@message) ->
handle: (req, res) =>
res.writeHead(200, {'Content-Type': 'text/plain'})
res.end(@message + 'n')
handler = new HttpRequestHandler "Hi There from CoffeeScript"
server = http.createServer(handler.handle)
server.listen(1338, '127.0.0.1')
console.log('Server running at http://127.0.0.1:1338/')
Classes :-)
Bound method
@crichardson
NodeJS
JavaScript
Reactor
pattern
Modules
@crichardson
About the Reactor pattern
Defined by Doug Schmidt in 1995
Pattern for writing scalable servers
Alternative to thread-per-connection model
Single threaded event loop dispatches events on
handles (e.g. sockets, file descriptors) to event handlers
@crichardson
Reactor pattern structure
Event Handler
handle_event(type)
get_handle()
Initiation Dispatcher
handle_events()
register_handler(h)
select(handlers)
for each h in handlers
h.handle_event(type)
end loop
handle
Synchronous Event
Demultiplexer
select()
owns
notifies
uses
handlers
@crichardson
Benefits:
Separation of concerns - event handlers separated
from low-level mechanism
More efficient - no thread context switching
Simplified concurrency - single threaded
@crichardson
Drawbacks:
Non-pre-emptive - handlers can’t block/take a long
time
Difficult to understand and debug - inverted flow of
control
@crichardson
NodeJS event loop implements
the Reactor pattern
@crichardson
Application code
Event-driven architecture
NodeJS event loop
Basic networking/file-system/etc.
HTTP DB driver ...
Event
listener
Callback
function
One
time
events
Recurring
events
@crichardson
Getting notified: Callback
example
var fs = require("fs");
function statFile(path) {
fs.stat(path, function (err, stat) {
if (err) {
console.log("Stat failed: " + path, err);
throw err;
}
console.log("stat result=" + path, stat);
});
};
By convention: first
param is error object
By convention: Last
arg is a callback
Callbacks are
good for one
time
notifications
@crichardson
Getting notified: event
listeners
EventEmitter class - inherit or use
Listener registration methods:
on(eventName, listener)
once(eventName, listener)
Emitting events
emit(eventName, args...)
‘error’ event = special case: no listener print stack trace and
exit!
Good for
recurring
events
@crichardson
Event listener example
var fs = require("fs");
var readStream = fs.createReadStream("events.js", {encoding: "utf-8"});
// ReadStream << ReadableStream << EventEmitter
readStream.on('open', function (fd) {
console.log("opened with fd=", fd);
});
// Node v0.10 has readable instead: this is deprecated
readStream.on('data', function (data) {
console.log("data=", data);
});
Register listener
Register listener
@crichardson
Callback hell
function times2(x, callback) {
setTimeout(function () {
callback(x * 2)}, 500);
}
function plus3(x, callback) {
setTimeout(function (){
callback(x + 3)}, 500);
}
function displayResult(z) {
console.log("The result is=", z);
}
function plus3AndThenTimes2(x, callback)
{
plus3(x, function (y) {
times2(y, callback)
})
}
plus3AndThenTimes2(10, displayResults);
function sum(a, b, callback) {
setTimeout(function () {
callback(a + b);
}, 500);
}
function plus3PlusTimes2(x, callback) {
var p3, t2;
function perhapsDone() {
if (p3 & t2)
sum(p3, t2, callback);
};
plus3(x, function (y) {
p3 = y;
perhapsDone();
});
times2(x, function (y) {
t2 = y;
perhapsDone();
});
}
plus3PlusTimes2(10, displayResult);
times2(plus3(x)) times2(x) + plus3(x)
@crichardson
Long running computations
Long running computation blocks event loop for
other requests
Need to run outside of main event loop
Options:
Community: web workers threads
Built-in: NodeJS child processes
@crichardson
Using child processes
var child = require('child_process').fork('child.js');
function sayHelloToChild() {
child.send({hello: "child"});
}
setTimeout(sayHelloToChild, 1000);
child.on('message', function(m) {
console.log('parent received:', m);
});
function kill() {
child.kill();
}
setTimeout(kill, 2000);
process.on('message', function (m) {
console.log("child received message=", m);
process.send({ihateyou: "you ruined my life"})
});
parent.js
child.js
Create child process
Send message to child
@crichardson
NodeJS
JavaScript
Reactor
pattern
Modules
Core built-in modules
Basic networking
HTTP(S)
Filesystem
Events
Timers
...
@crichardson
Thousands of community
developed modules
https://npmjs.org/
web frameworks, SQL/NoSQL
database, drivers, messaging, utilities...
@crichardson
What’s a module?
One or more JavaScript files
Optional native code:
Compiled during installation
JavaScript != systems programming language
Package.json - metadata including dependencies
exports.sayHello = function () {
console.log(“Hello”);
}
foo.js
@crichardson
Easy to install
$ npm install package-name
@crichardson
Easy to use
var http = require(“http”)
var server = http.createServer...
Core module OR
Path to file OR
module in node_modules
Module’s exports
@crichardson
Developing with NodeJS
modules
Core modules
Community modules
Your modules
Application code
@crichardson
Lots of modules BUT...
Variable quality
Multiple incomplete MySQL drivers, e.g. without
connection pooling!
Often abandoned
...
@crichardson
To summarize
NodeJS
JavaScript
Reactor patternModules
Flawed and
misunderstood
Scalable yet
costly and
often
unnecessary
Rich but
variable quality
@crichardson
Alternative technologies
Atmosphere - portable event delivery
Netty - asynchronous I/O
Vert.x
SpringSource’s Reactor project
...
@crichardson
So why care about
NodeJS?
Easy to write scalable network services
Easy to push events to the browser
Easy to get (small) stuff done
It has a role to play in modern
application architecture
@crichardson
Evolving from a monolithic
architecture....
WAR
Shipping
Service
Accounting
Service
Inventory
Service
StoreFrontUI
@crichardson
... to a micro-service architecture
Store front web application
shipping web application
inventory web application
Accounting
Service
StoreFrontUI
accounting web application
Shipping
Service
Inventory
Service
@crichardson
Browser
WAR
StoreFrontUI
Model
View Controller
Presentation layer evolution....
HTML / HTTP
+
JavaScript
@crichardson
Browser Web application
RESTful
EndpointsModel
View Controller
...Presentation layer evolution
JSON-REST
HTML 5 -
JavaScript
No elaborate, server-side web framework
required
Event
publisher
Events
Static
content
@crichardson
NodeJS as an API gateway
Browser
Service 1
Service 2
Message
Bus
HTML 5/
Java
Script
Socket.io
client
Events
RESTful WS
Server
application
Socket.io
server
Node JS
Service 3
RESTful
WS
@crichardson
Agenda
Overview of NodeJS
Building a front-end server with NodeJS
Taming tangled asynchronous code with promises
@crichardson
Serving static content
@crichardson
Using low-level APIs
var http = require('http'), path = require('path'), mime = require('mime'), fs = require("fs");
var server = http.createServer(function (req, res) {
var filePath;
if (req.url == '/') {
filePath = 'public/index.html';
} else {
filePath = 'public' + req.url;
}
fs.exists(filePath, function (exists) {
if (exists) {
res.writeHead(200, {"content-type": mime.lookup(path.basename(filePath)) });
fs.createReadStream(filePath).pipe(res);
} else {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.write('Error 404: resource not found.');
res.end();
}
});
}
);
server.listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
Uses file
extension
Cool!
@crichardson
Using the express web
framework
var express = require('express')
, http = require('http')
, app = express()
, server = http.createServer(app)
;
app.configure(function(){
app.use(express.static(__dirname + '/public'));
});
server.listen(8081);
@crichardson
RESTful web services
@crichardson
Implementing RESTful WS
with Express
var express = require('express'),
http = require('http'),
path = require('path');
var app = express();
var server = http.createServer(app);
app.get('/portfolio/:userId', function (req, res) {
var portfolio = retrievePortfolio(req.params.userId);
res.json(portfolio);
});
Easy URL
routing and
destructuring
@crichardson
Proxying to backend server
express = require('express')
request = require('request')
app = express.createServer()
proxyToBackend = (baseUrl) ->
(req, res) ->
callback = (error, response, body) -> console.log("error=", error)
originRequest = request(baseUrl + req.url, callback)
req.pipe(originRequest)
originRequest.pipe(res)
app.get('/restaurant/*', proxyToBackend('http://available-restaurant....com'))
app.post('/orders', proxyToBackend('http://restaurant-management...com'))
app.get('/orders', proxyToBackend(''http://restaurant-management...com'))
Returns a request handler
that proxies to baseUrl
@crichardson
Delivering events to the
browser
@crichardson
NodeJS clock example
Increments every
second
@crichardson
Socket.io - Server side
var express = require('express')
, http = require('http')
, app = express()
, server = http.createServer(app)
, io = require('socket.io').listen(server)
;
app.configure(function(){
app.use(express.static(__dirname + '/public'));
});
server.listen(8081);
io.sockets.on('connection', function (socket) {
	 var counter = 0;
	 function tick() {
	 	 counter = counter + 1;
	 	 socket.emit('tick', counter);
	 };
	 setInterval(tick, 1000);
});
handle new
connection
Send tick event to
browser every 1 sec
initializes socket.io
@crichardson
Socket.io - client side using the
knockout.js MVVM framework
var socket = io.connect(location.hostname);
function ClockModel() {
self.ticker = ko.observable(1);
socket.on('tick', function (data) {
self.ticker(data);
});
};
ko.applyBindings(new ClockModel());
<html>
<body>
The event is <span data-bind="text: ticker"></span>
<script src="/socket.io/socket.io.js"></script>
<script src="/knockout-2.0.0.js"></script>
<script src="/clock.js"></script>
</body>
</html>
clock.js
Connect to
socket.io
Subscribe
to tick event
Bind to model
Update
model
@crichardson
Distributed NodeJS clock
example
Browser NodeJS NodeJS
RabbitMQ
ProducerConsumer
socket.iosocket.io
@crichardson
AMQP Publisher
var amqp = require('amqp'),
amqpConnection = amqp.createConnection(...),
tickTockExchange;
function tick() {
var message = { tick: Date.now() };
tickTockExchange.publish("tickTock", message, {
mandatory: true,
contentType: "text/plain"
});
};
amqpConnection.on('ready',
function () {
tickTockExchange =
amqpConnection.exchange("tickTock",
options = {passive: false, type: 'fanout'});
tickTockExchange.on('open', function () { setInterval(tick, 1000) });
});
Connect to
AMQP
Ensure
exchange
exists
Publish
message
Initialize timer
@crichardson
AMQP socket.iovar express = require('express')
, http = require('http')
, amqp = require(‘amqp’)
....;
server.listen(8081);
...
var amqpCon = amqp.createConnection(...);
io.sockets.on('connection', function (socket) {
function amqpMessageHandler(message, headers, deliveryInfo) {
var m = JSON.parse(message.data.toString());
socket.emit(‘tick’, m);
};
amqpCon.queue(“”, {},
function(queue) {
queue.bind(“tickTock”, “”);
queue.subscribe(amqpMessageHandler);
});
});
Connect to
AMQP queue
Subscribe to
AMQP queue
Republish
as socket.io
event
https://github.com/cer/nodejs-clock
@crichardson
Agenda
Overview of NodeJS
Building a front-end server with NodeJS
Taming tangled asynchronous code with promises
@crichardson
Async code = callback hell
Scenarios:
Sequential: A B C
Fork and join: A and B C
Code quickly becomes very messy
@crichardson
Simplifying code with
Promises (a.k.a. Futures)
Functions return a promise - no callback parameter
A promise represents an eventual outcome
Use a library of functions for transforming and
composing promises
Promises/A+ specification - http://promises-aplus.github.io/promises-spec
when.js (part of cujo.js by SpringSource) is a popular
implementation
@crichardson
Taming callback hell 1
function times2(x) {
var deferred = when.defer();
setTimeout(function () {
deferred.resolve(x * 2)}, 500);
return deferred.promise;
}
times2(plus3(x)) Create a deferred
Return a promise
Eventually supply a value
function plus3AndThenTimes2(x) {
return plus3(x).then(times2);
}
plus3AndThenTimes2(10).
then(displayResult);
Transform value in
promise
function plus3(x) {
var deferred = when.defer();
setTimeout(function () {
deferred.resolve(x + 3) }, 500);
return deferred.promise;
}
Simpler, almost
synchronous-style code
@crichardson
Taming callback hell 2
function sum(a, b) {
var deferred = when.defer();
setTimeout(function () {
deferred.resolve(a + b);
}, 500);
return deferred.promise;
}
function plus3PlusTimes2(x) {
var p3 = plus3(x),
t2 = times2(x);
return when.join(p3, t2).spread(sum);
}
plus3PlusTimes2(10).then(displayResult);
times2(x) + plus3(x)
Combine results
of two promises
Call with array
elements as
arguments
@crichardson
Calling non-promise code
var deferred = when.defer();
fs.stat(path, function (err, statInfo) {
if (err)
deferred.reject(err);
else
deferred.resolve(statInfo);
}
var promise = deferred.promise;
var nodefn = require("when/node/function");
var promise = nodefn.call(fs.stat, path)
Hides
boilerplate
code
@crichardson
Filesystem scanner example
Read contents of directory
Use stat to determine if directory or file
Recurse on directories
Merge the results
@crichardson
Read contents of directory
function findFilesInDir(dir) {
var directoryContents =
nodefn.call(self.fs.readdir, dir);
...
}
Returns promise containing
an array of file names
@crichardson
Create absolute paths
function findFilesInDir(dir) {
var directoryContents = ...
var toAbsolute = join.bind(undefined, dir)
var absolutePaths = when.map(directoryContents, toAbsolute);
...
}
Partially apply
join()
@crichardson
Use stat to determine if
directory or file
function findFilesInDir(dir) {
var directoryContents = ...
var absolutePaths = ...
var statTasks = when.map(absolutePaths, makeStatTask);
var statResults = parallel(statTasks);
...
}
function makeStatTask(path) {
return function () {
function makeStatInfo(stats) {
return {path: path, isdir: stats.isDirectory(),
ctime: stats.ctime};
}
return nodefn.call(self.fs.stat, path).then(makeStatInfo);
};
}
Execute stats in
parallel
@crichardson
Recurse on directories
function findFilesInDir(dir) {
...
var statResults = ...;
var listOfListsOfFiles =
when.map(statResults, processStatInfo);
...
}
function processStatInfo(statInfo) {
if (statInfo.isdir) {
return findFilesInDir(statInfo.path);
} else {
return [statInfo.path];
}
}
Map each stat
result to a list of
files
@crichardson
Flatten array of arrays of file
paths
function findFilesInDir(dir) {
...
var listOfListsOfFiles = ...;
return when.reduce(listOfListsOfFiles,
concatenateLists, []);
}
function concatenateLists(currentResult, value, index, total)
{
return currentResult.concat(value);
}
@crichardson
Summary
JavaScript is a very flawed language
The asynchronous model is often unnecessary; very
constraining; and adds complexity
BUT despite those problems
Today, NodeJS is remarkably useful for building network-
focussed components
@crichardson
Questions?
@crichardson chris.richardson@springsource.com
http://plainoldobjects.com - code and slides
www.cloudfoundry.com

More Related Content

PDF
Building and deploying microservices with event sourcing, CQRS and Docker (Ha...
PDF
Microservices + Events + Docker = A Perfect Trio (dockercon)
PDF
Developing functional domain models with event sourcing (sbtb, sbtb2015)
PDF
Developing microservices with aggregates (melbourne)
PDF
Handling Eventual Consistency in JVM Microservices with Event Sourcing (javao...
PDF
Microservices in Java and Scala (sfscala)
PDF
#JaxLondon keynote: Developing applications with a microservice architecture
PDF
JavaOne2017: ACID Is So Yesterday: Maintaining Data Consistency with Sagas
Building and deploying microservices with event sourcing, CQRS and Docker (Ha...
Microservices + Events + Docker = A Perfect Trio (dockercon)
Developing functional domain models with event sourcing (sbtb, sbtb2015)
Developing microservices with aggregates (melbourne)
Handling Eventual Consistency in JVM Microservices with Event Sourcing (javao...
Microservices in Java and Scala (sfscala)
#JaxLondon keynote: Developing applications with a microservice architecture
JavaOne2017: ACID Is So Yesterday: Maintaining Data Consistency with Sagas

What's hot (20)

PDF
A Pattern Language for Microservices (@futurestack)
PDF
#hacksummit 2016 - event-driven microservices – Events on the outside, on the...
PDF
Building and deploying microservices with event sourcing, CQRS and Docker (Be...
PDF
Gluecon: Using sagas to maintain data consistency in a microservice architecture
PDF
Events on the outside, on the inside and at the core (jfokus jfokus2016)
PDF
Building and deploying microservices with event sourcing, CQRS and Docker (QC...
PDF
Developing microservices with aggregates (devnexus2017)
PDF
#JaxLondon: Building microservices with Scala, functional domain models and S...
PDF
Developing functional domain models with event sourcing (oakjug, sfscala)
PDF
Futures and Rx Observables: powerful abstractions for consuming web services ...
PDF
Microservices and Redis #redisconf Keynote
PDF
Building microservices with Scala, functional domain models and Spring Boot
PDF
QCONSF - ACID Is So Yesterday: Maintaining Data Consistency with Sagas
PDF
Developing event-driven microservices with event sourcing and CQRS (london Ja...
PDF
There is no such thing as a microservice! (oracle code nyc)
PDF
OReilly SACON2018 - Events on the outside, on the inside, and at the core
PDF
ArchSummit Shenzhen - Using sagas to maintain data consistency in a microserv...
PDF
Omnikron webbinar - Microservices: enabling the rapid, frequent, and reliable...
PDF
CQRS and Event Sourcing with Akka, Cassandra and RabbitMQ
PDF
A pattern language for microservices (melbourne)
A Pattern Language for Microservices (@futurestack)
#hacksummit 2016 - event-driven microservices – Events on the outside, on the...
Building and deploying microservices with event sourcing, CQRS and Docker (Be...
Gluecon: Using sagas to maintain data consistency in a microservice architecture
Events on the outside, on the inside and at the core (jfokus jfokus2016)
Building and deploying microservices with event sourcing, CQRS and Docker (QC...
Developing microservices with aggregates (devnexus2017)
#JaxLondon: Building microservices with Scala, functional domain models and S...
Developing functional domain models with event sourcing (oakjug, sfscala)
Futures and Rx Observables: powerful abstractions for consuming web services ...
Microservices and Redis #redisconf Keynote
Building microservices with Scala, functional domain models and Spring Boot
QCONSF - ACID Is So Yesterday: Maintaining Data Consistency with Sagas
Developing event-driven microservices with event sourcing and CQRS (london Ja...
There is no such thing as a microservice! (oracle code nyc)
OReilly SACON2018 - Events on the outside, on the inside, and at the core
ArchSummit Shenzhen - Using sagas to maintain data consistency in a microserv...
Omnikron webbinar - Microservices: enabling the rapid, frequent, and reliable...
CQRS and Event Sourcing with Akka, Cassandra and RabbitMQ
A pattern language for microservices (melbourne)
Ad

Similar to NodeJS: the good parts? A skeptic’s view (jax jax2013) (20)

PDF
NodeJS: the good parts? A skeptic’s view (devnexus2014)
PPTX
apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...
PDF
Consuming web services asynchronously with Futures and Rx Observables (svcc, ...
PDF
Futures and Rx Observables: powerful abstractions for consuming web services ...
PDF
NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)
KEY
Writing robust Node.js applications
PDF
Durable functions 2.0 (2019-10-10)
PPTX
Async Redux Actions With RxJS - React Rally 2016
PDF
Cервер на Go для мобильной стратегии
PDF
Node js introduction
PPTX
Developing web-apps like it's 2013
PDF
Store and Process Big Data with Hadoop and Cassandra
PPT
Expert JavaScript tricks of the masters
PDF
The powerful toolset of the go-mysql library
PDF
Joe Walker Interactivewebsites Cometand Dwr
PDF
Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
PPT
Spring data iii
PPTX
NodeJS
PDF
CouchDB Mobile - From Couch to 5K in 1 Hour
PDF
NodeJS: the good parts? A skeptic’s view (jmaghreb, jmaghreb2013)
NodeJS: the good parts? A skeptic’s view (devnexus2014)
apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...
Consuming web services asynchronously with Futures and Rx Observables (svcc, ...
Futures and Rx Observables: powerful abstractions for consuming web services ...
NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)
Writing robust Node.js applications
Durable functions 2.0 (2019-10-10)
Async Redux Actions With RxJS - React Rally 2016
Cервер на Go для мобильной стратегии
Node js introduction
Developing web-apps like it's 2013
Store and Process Big Data with Hadoop and Cassandra
Expert JavaScript tricks of the masters
The powerful toolset of the go-mysql library
Joe Walker Interactivewebsites Cometand Dwr
Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Spring data iii
NodeJS
CouchDB Mobile - From Couch to 5K in 1 Hour
NodeJS: the good parts? A skeptic’s view (jmaghreb, jmaghreb2013)
Ad

More from Chris Richardson (20)

PDF
The microservice architecture: what, why, when and how?
PDF
More the merrier: a microservices anti-pattern
PDF
YOW London - Considering Migrating a Monolith to Microservices? A Dark Energy...
PDF
Dark Energy, Dark Matter and the Microservices Patterns?!
PDF
Dark energy, dark matter and microservice architecture collaboration patterns
PDF
Scenarios_and_Architecture_SkillsMatter_April_2022.pdf
PDF
Using patterns and pattern languages to make better architectural decisions
PDF
iSAQB gathering 2021 keynote - Architectural patterns for rapid, reliable, fr...
PDF
Events to the rescue: solving distributed data problems in a microservice arc...
PDF
A pattern language for microservices - June 2021
PDF
QConPlus 2021: Minimizing Design Time Coupling in a Microservice Architecture
PDF
Mucon 2021 - Dark energy, dark matter: imperfect metaphors for designing micr...
PDF
Designing loosely coupled services
PDF
Microservices - an architecture that enables DevOps (T Systems DevOps day)
PDF
DDD SoCal: Decompose your monolith: Ten principles for refactoring a monolith...
PDF
Decompose your monolith: Six principles for refactoring a monolith to microse...
PDF
TDC2020 - The microservice architecture: enabling rapid, reliable, frequent a...
PDF
Overview of the Eventuate Tram Customers and Orders application
PDF
An overview of the Eventuate Platform
PDF
#DevNexus202 Decompose your monolith
The microservice architecture: what, why, when and how?
More the merrier: a microservices anti-pattern
YOW London - Considering Migrating a Monolith to Microservices? A Dark Energy...
Dark Energy, Dark Matter and the Microservices Patterns?!
Dark energy, dark matter and microservice architecture collaboration patterns
Scenarios_and_Architecture_SkillsMatter_April_2022.pdf
Using patterns and pattern languages to make better architectural decisions
iSAQB gathering 2021 keynote - Architectural patterns for rapid, reliable, fr...
Events to the rescue: solving distributed data problems in a microservice arc...
A pattern language for microservices - June 2021
QConPlus 2021: Minimizing Design Time Coupling in a Microservice Architecture
Mucon 2021 - Dark energy, dark matter: imperfect metaphors for designing micr...
Designing loosely coupled services
Microservices - an architecture that enables DevOps (T Systems DevOps day)
DDD SoCal: Decompose your monolith: Ten principles for refactoring a monolith...
Decompose your monolith: Six principles for refactoring a monolith to microse...
TDC2020 - The microservice architecture: enabling rapid, reliable, frequent a...
Overview of the Eventuate Tram Customers and Orders application
An overview of the Eventuate Platform
#DevNexus202 Decompose your monolith

Recently uploaded (20)

PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
KodekX | Application Modernization Development
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Electronic commerce courselecture one. Pdf
PPTX
Big Data Technologies - Introduction.pptx
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PPTX
Cloud computing and distributed systems.
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
Mobile App Security Testing_ A Comprehensive Guide.pdf
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Reach Out and Touch Someone: Haptics and Empathic Computing
KodekX | Application Modernization Development
NewMind AI Weekly Chronicles - August'25 Week I
Encapsulation_ Review paper, used for researhc scholars
Review of recent advances in non-invasive hemoglobin estimation
Electronic commerce courselecture one. Pdf
Big Data Technologies - Introduction.pptx
Building Integrated photovoltaic BIPV_UPV.pdf
Cloud computing and distributed systems.
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Digital-Transformation-Roadmap-for-Companies.pptx
Per capita expenditure prediction using model stacking based on satellite ima...
Programs and apps: productivity, graphics, security and other tools
MIND Revenue Release Quarter 2 2025 Press Release
Diabetes mellitus diagnosis method based random forest with bat algorithm

NodeJS: the good parts? A skeptic’s view (jax jax2013)

  • 1. @crichardson NodeJS: the good parts? A skeptic’s view Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson chris.richardson@springsource.com http://plainoldobjects.com
  • 2. @crichardson Presentation goal How a curmudgeonly server-side Java developer discovered an appreciation for NodeJS and JavaScript
  • 8. @crichardson vmc push About-Chris Developer Advocate Signup at http://cloudfoundry.com
  • 9. @crichardson Agenda Overview of NodeJS Building a front-end server with NodeJS Taming tangled asynchronous code with promises
  • 12. @crichardson NodeJS Hello World app.js $ node app.js $ curl http://localhost:1337 http://nodejs.org/ Load a module request handler
  • 15. @crichardson Dynamic, weakly-typed Dynamic: Types are associated with values - not variables Define new program elements at runtime Weakly typed: Leave out arguments to methods Access non-existent object properties Weird implicit conversions: 99 == “99”! truthy and falsy values Comprehensive tests are essential
  • 16. @crichardson JavaScript is object-oriented > var fred = {name: “Fred”, gender: “Male”}; undefined > fred.name “Fred” > console.log("reading age=" + fred.age); reading age=undefined undefined > fred.age = 99; 99 > fred { name: 'Fred', gender: 'Male', age: 99 } > delete fred.age true > fred { name: 'Fred', gender: 'Male' } Unordered key-value pairs Keys = properties Add property Delete property
  • 17. @crichardson JavaScript “classes” function Person(name) { this.name = name; } Person.prototype.sayHello = function () { console.log("Hello " + this.name); }; var chris = new Person("Chris"); chris.sayHello(); Looks like a constructor?!? What’s that?!?! This Java-like syntax is a mess because JavaScript isn’t class based Looks familiar
  • 18. @crichardson overrides JavaScript is a prototypal language __proto__ a 99 __proto__ a 100 b 200 inherited Prototype X Y
  • 19. @crichardson Prototypal code $ node > var person = {}; undefined > person.sayHello = function () { console.log("Hello " + this.name); }; [Function] > var chris = Object.create(person, {name: {value: "Chris"}}); undefined > var sarah = Object.create(person, {name: {value: "Sarah"}}); undefined > chris.sayHello(); Hello Chris undefined > sarah.sayHello(); Hello Sarah undefined > chris.sayHello = function () { console.log("Hello mate: " + this.name); }; [Function] > chris.sayHello(); Hello mate: Chris undefined Not defined here prototype properties
  • 20. @crichardson JavaScript is Functional function makeGenerator(nextFunction) { var value = 0; return function() { var current = value; value = nextFunction(value); return current; }; } var inc = makeGenerator(function (x) {return x + 1; }); > inc() 0 > inc() 1 Pass function as an argument Return a function closure
  • 21. @crichardson Partial function application > var join = require("path").join; undefined > join("/x", "y") '/x/y' > var withinx = join.bind(undefined, "/x"); undefined > withinx("y"); '/x/y' > partially apply join
  • 22. @crichardson Created in a hurry with the goal of looking like Java The ‘Java...’ name creates expectations that it can’t satisfy Fake classes: Hides prototypes BUT still seems weird global namespace scope of vars is confusing Missing return statement = confusion ‘function’ is really verbose ‘this’ is dynamically scoped
  • 23. @crichardson JavaScript is the only way to get things done in the browser
  • 24. @crichardson Stockholm syndrome “Stockholm syndrome ... is a psychological phenomenon in which hostages ... have positive feelings toward their captors, sometimes to the point of defending them...” http://en.wikipedia.org/wiki/Stockholm_syndrome
  • 25. @crichardson Martin Fowler once said: "...I'm one of those who despairs that a language with such deep flaws plays such an important role in computation. Still the consequence of this is that we must take javascript seriously as a first-class language and concentrate on how to limit the damage its flaws cause. ...." http://martinfowler.com/bliki/gotoAarhus2012.html
  • 27. @crichardson Use a better language that compiles to JavaScript TypeScript Typed parameters and fields Classes and interfaces (dynamic structural typing) Dart Class-based OO Optional static typing Bidirectional binding with DOM elements
  • 28. @crichardson CoffeeScript Hello World http = require('http') class HttpRequestHandler constructor: (@message) -> handle: (req, res) => res.writeHead(200, {'Content-Type': 'text/plain'}) res.end(@message + 'n') handler = new HttpRequestHandler "Hi There from CoffeeScript" server = http.createServer(handler.handle) server.listen(1338, '127.0.0.1') console.log('Server running at http://127.0.0.1:1338/') Classes :-) Bound method
  • 30. @crichardson About the Reactor pattern Defined by Doug Schmidt in 1995 Pattern for writing scalable servers Alternative to thread-per-connection model Single threaded event loop dispatches events on handles (e.g. sockets, file descriptors) to event handlers
  • 31. @crichardson Reactor pattern structure Event Handler handle_event(type) get_handle() Initiation Dispatcher handle_events() register_handler(h) select(handlers) for each h in handlers h.handle_event(type) end loop handle Synchronous Event Demultiplexer select() owns notifies uses handlers
  • 32. @crichardson Benefits: Separation of concerns - event handlers separated from low-level mechanism More efficient - no thread context switching Simplified concurrency - single threaded
  • 33. @crichardson Drawbacks: Non-pre-emptive - handlers can’t block/take a long time Difficult to understand and debug - inverted flow of control
  • 34. @crichardson NodeJS event loop implements the Reactor pattern
  • 35. @crichardson Application code Event-driven architecture NodeJS event loop Basic networking/file-system/etc. HTTP DB driver ... Event listener Callback function One time events Recurring events
  • 36. @crichardson Getting notified: Callback example var fs = require("fs"); function statFile(path) { fs.stat(path, function (err, stat) { if (err) { console.log("Stat failed: " + path, err); throw err; } console.log("stat result=" + path, stat); }); }; By convention: first param is error object By convention: Last arg is a callback Callbacks are good for one time notifications
  • 37. @crichardson Getting notified: event listeners EventEmitter class - inherit or use Listener registration methods: on(eventName, listener) once(eventName, listener) Emitting events emit(eventName, args...) ‘error’ event = special case: no listener print stack trace and exit! Good for recurring events
  • 38. @crichardson Event listener example var fs = require("fs"); var readStream = fs.createReadStream("events.js", {encoding: "utf-8"}); // ReadStream << ReadableStream << EventEmitter readStream.on('open', function (fd) { console.log("opened with fd=", fd); }); // Node v0.10 has readable instead: this is deprecated readStream.on('data', function (data) { console.log("data=", data); }); Register listener Register listener
  • 39. @crichardson Callback hell function times2(x, callback) { setTimeout(function () { callback(x * 2)}, 500); } function plus3(x, callback) { setTimeout(function (){ callback(x + 3)}, 500); } function displayResult(z) { console.log("The result is=", z); } function plus3AndThenTimes2(x, callback) { plus3(x, function (y) { times2(y, callback) }) } plus3AndThenTimes2(10, displayResults); function sum(a, b, callback) { setTimeout(function () { callback(a + b); }, 500); } function plus3PlusTimes2(x, callback) { var p3, t2; function perhapsDone() { if (p3 & t2) sum(p3, t2, callback); }; plus3(x, function (y) { p3 = y; perhapsDone(); }); times2(x, function (y) { t2 = y; perhapsDone(); }); } plus3PlusTimes2(10, displayResult); times2(plus3(x)) times2(x) + plus3(x)
  • 40. @crichardson Long running computations Long running computation blocks event loop for other requests Need to run outside of main event loop Options: Community: web workers threads Built-in: NodeJS child processes
  • 41. @crichardson Using child processes var child = require('child_process').fork('child.js'); function sayHelloToChild() { child.send({hello: "child"}); } setTimeout(sayHelloToChild, 1000); child.on('message', function(m) { console.log('parent received:', m); }); function kill() { child.kill(); } setTimeout(kill, 2000); process.on('message', function (m) { console.log("child received message=", m); process.send({ihateyou: "you ruined my life"}) }); parent.js child.js Create child process Send message to child
  • 43. Core built-in modules Basic networking HTTP(S) Filesystem Events Timers ...
  • 44. @crichardson Thousands of community developed modules https://npmjs.org/ web frameworks, SQL/NoSQL database, drivers, messaging, utilities...
  • 45. @crichardson What’s a module? One or more JavaScript files Optional native code: Compiled during installation JavaScript != systems programming language Package.json - metadata including dependencies exports.sayHello = function () { console.log(“Hello”); } foo.js
  • 46. @crichardson Easy to install $ npm install package-name
  • 47. @crichardson Easy to use var http = require(“http”) var server = http.createServer... Core module OR Path to file OR module in node_modules Module’s exports
  • 48. @crichardson Developing with NodeJS modules Core modules Community modules Your modules Application code
  • 49. @crichardson Lots of modules BUT... Variable quality Multiple incomplete MySQL drivers, e.g. without connection pooling! Often abandoned ...
  • 50. @crichardson To summarize NodeJS JavaScript Reactor patternModules Flawed and misunderstood Scalable yet costly and often unnecessary Rich but variable quality
  • 51. @crichardson Alternative technologies Atmosphere - portable event delivery Netty - asynchronous I/O Vert.x SpringSource’s Reactor project ...
  • 52. @crichardson So why care about NodeJS? Easy to write scalable network services Easy to push events to the browser Easy to get (small) stuff done It has a role to play in modern application architecture
  • 53. @crichardson Evolving from a monolithic architecture.... WAR Shipping Service Accounting Service Inventory Service StoreFrontUI
  • 54. @crichardson ... to a micro-service architecture Store front web application shipping web application inventory web application Accounting Service StoreFrontUI accounting web application Shipping Service Inventory Service
  • 56. @crichardson Browser Web application RESTful EndpointsModel View Controller ...Presentation layer evolution JSON-REST HTML 5 - JavaScript No elaborate, server-side web framework required Event publisher Events Static content
  • 57. @crichardson NodeJS as an API gateway Browser Service 1 Service 2 Message Bus HTML 5/ Java Script Socket.io client Events RESTful WS Server application Socket.io server Node JS Service 3 RESTful WS
  • 58. @crichardson Agenda Overview of NodeJS Building a front-end server with NodeJS Taming tangled asynchronous code with promises
  • 60. @crichardson Using low-level APIs var http = require('http'), path = require('path'), mime = require('mime'), fs = require("fs"); var server = http.createServer(function (req, res) { var filePath; if (req.url == '/') { filePath = 'public/index.html'; } else { filePath = 'public' + req.url; } fs.exists(filePath, function (exists) { if (exists) { res.writeHead(200, {"content-type": mime.lookup(path.basename(filePath)) }); fs.createReadStream(filePath).pipe(res); } else { res.writeHead(404, {'Content-Type': 'text/plain'}); res.write('Error 404: resource not found.'); res.end(); } }); } ); server.listen(1337, '127.0.0.1'); console.log('Server running at http://127.0.0.1:1337/'); Uses file extension Cool!
  • 61. @crichardson Using the express web framework var express = require('express') , http = require('http') , app = express() , server = http.createServer(app) ; app.configure(function(){ app.use(express.static(__dirname + '/public')); }); server.listen(8081);
  • 63. @crichardson Implementing RESTful WS with Express var express = require('express'), http = require('http'), path = require('path'); var app = express(); var server = http.createServer(app); app.get('/portfolio/:userId', function (req, res) { var portfolio = retrievePortfolio(req.params.userId); res.json(portfolio); }); Easy URL routing and destructuring
  • 64. @crichardson Proxying to backend server express = require('express') request = require('request') app = express.createServer() proxyToBackend = (baseUrl) -> (req, res) -> callback = (error, response, body) -> console.log("error=", error) originRequest = request(baseUrl + req.url, callback) req.pipe(originRequest) originRequest.pipe(res) app.get('/restaurant/*', proxyToBackend('http://available-restaurant....com')) app.post('/orders', proxyToBackend('http://restaurant-management...com')) app.get('/orders', proxyToBackend(''http://restaurant-management...com')) Returns a request handler that proxies to baseUrl
  • 67. @crichardson Socket.io - Server side var express = require('express') , http = require('http') , app = express() , server = http.createServer(app) , io = require('socket.io').listen(server) ; app.configure(function(){ app.use(express.static(__dirname + '/public')); }); server.listen(8081); io.sockets.on('connection', function (socket) { var counter = 0; function tick() { counter = counter + 1; socket.emit('tick', counter); }; setInterval(tick, 1000); }); handle new connection Send tick event to browser every 1 sec initializes socket.io
  • 68. @crichardson Socket.io - client side using the knockout.js MVVM framework var socket = io.connect(location.hostname); function ClockModel() { self.ticker = ko.observable(1); socket.on('tick', function (data) { self.ticker(data); }); }; ko.applyBindings(new ClockModel()); <html> <body> The event is <span data-bind="text: ticker"></span> <script src="/socket.io/socket.io.js"></script> <script src="/knockout-2.0.0.js"></script> <script src="/clock.js"></script> </body> </html> clock.js Connect to socket.io Subscribe to tick event Bind to model Update model
  • 69. @crichardson Distributed NodeJS clock example Browser NodeJS NodeJS RabbitMQ ProducerConsumer socket.iosocket.io
  • 70. @crichardson AMQP Publisher var amqp = require('amqp'), amqpConnection = amqp.createConnection(...), tickTockExchange; function tick() { var message = { tick: Date.now() }; tickTockExchange.publish("tickTock", message, { mandatory: true, contentType: "text/plain" }); }; amqpConnection.on('ready', function () { tickTockExchange = amqpConnection.exchange("tickTock", options = {passive: false, type: 'fanout'}); tickTockExchange.on('open', function () { setInterval(tick, 1000) }); }); Connect to AMQP Ensure exchange exists Publish message Initialize timer
  • 71. @crichardson AMQP socket.iovar express = require('express') , http = require('http') , amqp = require(‘amqp’) ....; server.listen(8081); ... var amqpCon = amqp.createConnection(...); io.sockets.on('connection', function (socket) { function amqpMessageHandler(message, headers, deliveryInfo) { var m = JSON.parse(message.data.toString()); socket.emit(‘tick’, m); }; amqpCon.queue(“”, {}, function(queue) { queue.bind(“tickTock”, “”); queue.subscribe(amqpMessageHandler); }); }); Connect to AMQP queue Subscribe to AMQP queue Republish as socket.io event https://github.com/cer/nodejs-clock
  • 72. @crichardson Agenda Overview of NodeJS Building a front-end server with NodeJS Taming tangled asynchronous code with promises
  • 73. @crichardson Async code = callback hell Scenarios: Sequential: A B C Fork and join: A and B C Code quickly becomes very messy
  • 74. @crichardson Simplifying code with Promises (a.k.a. Futures) Functions return a promise - no callback parameter A promise represents an eventual outcome Use a library of functions for transforming and composing promises Promises/A+ specification - http://promises-aplus.github.io/promises-spec when.js (part of cujo.js by SpringSource) is a popular implementation
  • 75. @crichardson Taming callback hell 1 function times2(x) { var deferred = when.defer(); setTimeout(function () { deferred.resolve(x * 2)}, 500); return deferred.promise; } times2(plus3(x)) Create a deferred Return a promise Eventually supply a value function plus3AndThenTimes2(x) { return plus3(x).then(times2); } plus3AndThenTimes2(10). then(displayResult); Transform value in promise function plus3(x) { var deferred = when.defer(); setTimeout(function () { deferred.resolve(x + 3) }, 500); return deferred.promise; } Simpler, almost synchronous-style code
  • 76. @crichardson Taming callback hell 2 function sum(a, b) { var deferred = when.defer(); setTimeout(function () { deferred.resolve(a + b); }, 500); return deferred.promise; } function plus3PlusTimes2(x) { var p3 = plus3(x), t2 = times2(x); return when.join(p3, t2).spread(sum); } plus3PlusTimes2(10).then(displayResult); times2(x) + plus3(x) Combine results of two promises Call with array elements as arguments
  • 77. @crichardson Calling non-promise code var deferred = when.defer(); fs.stat(path, function (err, statInfo) { if (err) deferred.reject(err); else deferred.resolve(statInfo); } var promise = deferred.promise; var nodefn = require("when/node/function"); var promise = nodefn.call(fs.stat, path) Hides boilerplate code
  • 78. @crichardson Filesystem scanner example Read contents of directory Use stat to determine if directory or file Recurse on directories Merge the results
  • 79. @crichardson Read contents of directory function findFilesInDir(dir) { var directoryContents = nodefn.call(self.fs.readdir, dir); ... } Returns promise containing an array of file names
  • 80. @crichardson Create absolute paths function findFilesInDir(dir) { var directoryContents = ... var toAbsolute = join.bind(undefined, dir) var absolutePaths = when.map(directoryContents, toAbsolute); ... } Partially apply join()
  • 81. @crichardson Use stat to determine if directory or file function findFilesInDir(dir) { var directoryContents = ... var absolutePaths = ... var statTasks = when.map(absolutePaths, makeStatTask); var statResults = parallel(statTasks); ... } function makeStatTask(path) { return function () { function makeStatInfo(stats) { return {path: path, isdir: stats.isDirectory(), ctime: stats.ctime}; } return nodefn.call(self.fs.stat, path).then(makeStatInfo); }; } Execute stats in parallel
  • 82. @crichardson Recurse on directories function findFilesInDir(dir) { ... var statResults = ...; var listOfListsOfFiles = when.map(statResults, processStatInfo); ... } function processStatInfo(statInfo) { if (statInfo.isdir) { return findFilesInDir(statInfo.path); } else { return [statInfo.path]; } } Map each stat result to a list of files
  • 83. @crichardson Flatten array of arrays of file paths function findFilesInDir(dir) { ... var listOfListsOfFiles = ...; return when.reduce(listOfListsOfFiles, concatenateLists, []); } function concatenateLists(currentResult, value, index, total) { return currentResult.concat(value); }
  • 84. @crichardson Summary JavaScript is a very flawed language The asynchronous model is often unnecessary; very constraining; and adds complexity BUT despite those problems Today, NodeJS is remarkably useful for building network- focussed components