Introduction
To
Node & Express

Before to start...

Some companies that uses Node...

JavaScript

Dynamic, object-oriented, prototype-oriented inheritance and general-purpose programming language

Node

JavaScript runtime built on the Chome's V8 JavaScript engine (brings JavaScript to the server side).

  • First class networking & streaming API.
  • Great standard libraries for interfacing with the OS, filesystem, etc…
  • Support for compiled binary modules when you need to extend Node’s capabilities with a lower-level language like C++.
  • Easy to get started.

JavaScript/Node

Some words JavaScript programmers love to repeat...

  • Single thread of execution
  • Event Driven
  • Non-Blocking I/O
  • Event loop

...and the winner:

Don't block the event loop

JavaScript/Node

  • Single thread of execution:
    • your code runs on the same thread
      • ​Avoid long time synchronous tasks (make them asynchronous)
    • simplifies programming

JavaScript/Node

  • Event Driven:
    • the flow of the program is determined by the events

JavaScript/Node

  • Non-Blocking I/O:
    • asynchronous operations run in a pool of threads
    • your thread is not blocked and can always work on some tasks

Node

  • Event loop:
    • decouples the caller from the response (callbacks)
    • the result of I/O operations are queued to be executed

JavaScript

# Python
import requests

r = requests.get('http://somewhere')
print r.text
print "I come after the request"
// JavaScript
var request = require('request');

request('http://somewhere', function (error, response, body) {
  console.log(body);
})
console.log('I come after the request');

Synchronous

Asynchronous

Node/JavaScript

Supposing task 3 depends on the response of task 2:

Synchronous

Asynchronous

Let's Go !!!

Node.js

JavaScript in the server side...

Node

JavaScript runtime built on the Chome's V8 JavaScript engine (brings JavaScript to the server side).

V8 compiles JavaScript source code directly into machine code when it is first executed.

There are no intermediate byte codes, no interpreter.

Node

  • First class networking & streaming API.
  • Great standard libraries for interfacing with the OS, filesystem, etc…
  • Support for compiled binary modules when you need to extend Node’s capabilities with a lower-level language like C++.
  • Easy to get started.

Our first Node app...

console.log('Node rocks !!!');
$ node ./app.js

Execute it:

./app.js

Your first Node server...

// Load the http module to create an http server.
var http = require('http');

// Listener to respond with Hello World to all requests.
function listener (request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.end("Hello World\n");
}

// Create our HTTP server 
var server = http.createServer(listener);

// Listen on port 8000, IP defaults to 127.0.0.1
server.listen(8000);

// Put a friendly message on the terminal
console.log("Server running at http://127.0.0.1:8000/");

In day to day code you'll probably see:

var server = http.createServer(function (request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.end("Hello World\n");
});

Some things you need to know

  • Single process, single CPU
  • If the process crashes the application is stopped (like any app., this is not an app server !!!)

 

  • Recommended to run with a process manager (like PM2) to run in cluster mode

 Modules...

  • Node offers core modules: fs, utils, http, ...
  • Imported using the 'require' keyword
  • Export thing with ' module.exports'
  • Cached after first time loaded
  • Core modules vs local modules:
    • require('foo'): Load node's core module or module installed in 'node_modules' folder.
    • require('./foo'): Load local module
  • Third party modules can easily be installed with a package manager (like npm or yarn)
    • npm install some_module
    • npm uninstall some_module

Working with modules...

var Service = {
    someMethod: function() {
        console.log('I made something');
    };
};

module.exports = Service;
var Service = require('./Service');

Service.someMethod();

var Service2 = require('./Service');

Service2.someMethod();

./service.js

./app.js

Service === Service2

Working with modules...

function Service() {
    this.someMethod() {
        console.log('I made something');
    }
};

module.exports = new Service();
var Service = require('./Service');

Service.someMethod();

var Service2 = require('./Service');

Service2.someMethod();

./service.js

./app.js

Service === Service2

Working with modules...

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = function() {
        return this.color + ' ' + this.type + ' apple';
    };
}

module.exports = Apple;
var Apple1 = require('./apple');
var apple1 = new Apple1('monarch');

var Apple2 = require('./apple');
var apple2 = new Apple2('pink');

./apple.js

./app.js

Apple1 === Apple2

apple1 !== apple2

The request...

Instance of http.IncomingMessage

var method = request.method;    // GET
var url = request.url;          // /endpoint?param=value
var headers = request.headers;
// { 
//   'user-agent': 'curl/7.22.0',
//   host: '127.0.0.1:8000',
//   accept: '*/*'
// }

var body = [];

request.on('error', function(err) {
  console.error(err);
}).on('data', function(chunk) {
  body.push(chunk);
}).on('end', function() {
  body = Buffer.concat(body).toString();
});

console.log(body);

The response...

Instance of http.ServerResponse

response.statusCode = 200;
response.setHeader('Content-Type', 'application/json');

var responseBody = {
  headers: headers,
  method: method,
  url: url,
  body: body
};

response.write(JSON.stringify(responseBody));
response.end();

Example: echo server

Respond with same data when accesing GET /echo 

var http = require('http');

http.createServer(function(request, response) {

  request.on('error', function(err) {
    console.error(err);
    response.statusCode = 400;
    response.end();
  });

  response.on('error', function(err) {
    console.error(err);
  });

  if (request.method === 'GET' && request.url === '/echo') {
    request.pipe(response);
  } else {
    response.statusCode = 404;
    response.end();
  }

}).listen(8080);

First class networking and streaming but...

it can be a bit cumbersome working with core Node.js

Express.js

Express

Fast, unopinionated, minimalist web framework

 

  • Middlewares !!!
  • Simplify working with routes and params
  • Improves Node request/response with new properties and methods

Your first Express server...

var express = require('express')
var app = express()

app.get('/hello/:name', function (req, res) {
  res.send('Hello ' + req.params.name)
})

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})

Middleware...

A function that have access to the

  • request
  • response
  • next middleware

Middlewares can:

  • Modify request and response
  • End request-response cycle
  • Call next middleware
function myBusiness(req, res, next) {
  req.customData = {
    token: 'sometoken',
    data: 'somedatahere',
  };

  if (req.params.name === 'bye') {
    res.status('200')
      .send('Bye !!!');
  }

  next();
}

// Attach middleware to a path
app.get('/dosomething', myBusines);

Middlewares

  • Chainable, to separate logic processing requests
  • Applied in their binding order
var express = require('express')
var morgan = require('morgan')
var bodyParser = require('body-parser')
var cookieParser = require('cookie-parser')

var app = express()

app.use(morgan('combined'))   // 127.0.0.1 - - [03/JUL/2015:16:54:25 +0000] "GET / HTTP/1.1" 200 13 "-" "MOZILLA/5.0 (MACINTOSH; INTEL MAC OS X 10_10_4) APPLEWEBKIT/600.7.12 (KHTML, LIKE GECKO) VERSION/8.0.7 SAFARI/600.7.12"
app.use(bodyParser.json())    // Parse request body content and stores in req.body object
app.use(cookieParser())       // Parse cookies and stores in req.cookies object
app.use(express.static('public'))    // Serve static files from 'public' folder

Routers

var app = express()
var userRouter = app.route('/users')

userRouter.get('/:id(\\d+)', function(req, res, next) { ... });
userRouter.post('/:id(\\d+)', function(req, res, next) { ... });
userRouter.delete('/:id(\\d+)', function(req, res, next) { ... });


// Attach router to the app
app.use(userRouter);

Allow to create groups of actions...

Improved request & response

  • req.body
  • req.cookies
  • req.params
  • req.query
  • req.ip / req.ips
  • req.get('header_name')

Express request and response object has additional methods and properties than the request and response methods of the node callbacks

  • res.cookie(key, value)
  • res.set(header, value)
  • res.json()
  • res.write()
  • res.end()
  • res.send()
  • res.sendFile()
  • res.status()
  • res.redirect()

Types of middlewares

  • Application-level
    • Attached to the express app object

 

  • Router-level
    • Attached to a router

 

  • Error handling
    • Designed to deal with errors
app.get('/something', doSomething);
const router = app.router('/users')

router.get('/products', doSomething)

app.use(router)

Error handler middleware

Similar to a normal middleware but has four parameters:

  • error
  • request
  • response
  • next middleware
app.get('/dosomething', function(req, res, next) {
  res.write('This is...');
  next();
});

app.get('/dosomething', function(req, res, next) {
  res.status(200).send('something !!!');
});

// Handle 404
app.use(function(req, res, next) {
  res.status(404).end();
});

// Handle errors
app.use(function(err, req, res, next) {
  res.status(500)
  res.render('error', { error: err })
});

Express will catch errors and send to any middleware registered with four parameters.

Defining routes...

  • router.METHOD(path, [callback, ...] callback)
  • router.use([path], [callback, ...] callback)
  • router.all(path, [callback, ...] callback)
app.use(function(req, res, next) {
  // Runs always 
});
  
app.all('/users', function(req, res, next) {
  // Runs on all HTTP verbs
})

app.get('/users', function(req, res, next) {
  // Runs for GET method
})

app.post('/users', function(req, res, next) {
  // Runs for POST method
});

app.get('/users', middleware1, middleware2);

NOTE: Invoke next('route') to bypass remaining route callbacks

Defining routes...

  • router.param(name, callback)
    • Executed when a given parameter exists in the path
router.param('id', function (req, res, next, id) {
  var user = ... // Retrive user from DB
  req.user = user;
  next();
});

router.get('/user/:id', function (req, res, next) {
  var user = req.user;
  // Do something with the user
  ...
});

Questions ?

Introduction to Node & Express

By Antonio Santiago

Introduction to Node & Express

High level description of Node and Express focusing on the main aspects.

  • 5,609