Introduction
To
Node & Express
Before to start...
Some companies that uses Node...
JavaScript
Dynamic, object-oriented, prototype-oriented inheritance and general-purpose programming language
- One of the world's most popular programming languages
- Notorious for being the world's most misunderstood 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
-
your code runs on the same thread
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');
- In summary, JS is well suited for asynchronous programming (not confuse with concurrency)
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
...
});