Node.js Web Frameworks
- By Me
Today













Topics
- History
- Numbers
- TL; DR
- Hello World
- Routing
- Validating parameters
Topics
- History
- Numbers
- TL; DR
- Hello World
- Routing
- Validating parameters

Started in 2009 from TJ Holowaychuk
In 2014 sponsorship passes to Strongloop
In 2016 Express goes under the Node Foundation umbrella
In 2015 IBM acquired Strongloop

Started in 2013 from TJ Holowaychuk









$2M Investment
Created on August 6, 2011 by Eran Hammer a member of WalmartLabs
Initially dependent on Expressjs
{
"name" : "hapi",
"description" : "HTTP API Server framework based on Express",
"version" : "0.1.1",
"author" : "Eran Hammer-Lahav <eran@hueniverse.com>",
"repository" : "git://github.com/walmartlabs/hapi",
"main" : "index",
"keywords": ["http", "mac", "authentication", "oauth", "api", "express"],
"engines" : {"node" : ">=0.6.0"},
"dependencies": {
"express": "2.x.x",
"mac": "0.x.x",
"validator": "0.x.x",
"emailjs": "0.x.x",
"sugar": "1.1.x"
}
}
Topics
- History
- Numbers
- TL; DR
- Hello World
- Routing
- Validating parameters


23,286 Stars
"version": "4.13.4"
25 dependencies
8000+ dependents


9,019 Stars
"version": "1.1.2"
23 dependencies
400+ dependents


5,515 Stars
"version": "13.0.0"
19 dependencies
300+ dependents
Topics
- History
- Numbers
- TL; DR
- Hello World
- Routing
- Validating parameters

Express.js is the minimal/simplest one with a wide community provided middlewares for all kind of purposes

Minimal too with a "futuristic" look, based upon es6 generator functions making your code easier to argue about with its synchronous looking, async code

Hapi.js is the rails like framework with a configuration orientation that when everything is configured things will "fall" in place (strong point is also Joi for validation of all kind of payloads)
Topics
- History
- Numbers
- TL; DR
- Hello Worlds
- Routing
- Validating parameters

// npm install --save express
var app = require("express")();
var port = 3000;
app.get("*", (req, res) => res.send("Hello World!"))
app.listen(port, () => {
console.log("Listening in port " + port);
});

// npm install --save koa
var koa = require('koa');
app = koa();
app.use(function *(){
this.body = 'Hello World';
});
app.listen(3000);

const Hapi = require('hapi');
const server = new Hapi.Server();
server.connection({ port: 3000 });
server.start((err) => {
if (err) {
throw err;
}
console.log('Server running at:', server.info.uri);
});
Topics
- History
- Numbers
- TL; DR
- Hello Worlds
- Routing
- Validating parameters

Has a built in router
var express = require("express");
var app = express();
var userRouter = express.Router();
// The authentication middleware will be used
// only for the routes attached to the userRouter
userRouter.use(userAuthenticationMiddleware);
userRouter.get("/login", userController.login);
userRouter.get("/logout", userController.logout);
userRouter.get("/register", userController.renderregister);
userRouter.post("/register", userController.register);
app.use("/user", userRouter);

Has no built in router
//npm install koa-route
var koa = require("koa");
var router = require("koa-route");
var app = koa();
// in the controller function
// this is bound to the application context
app.use(router.get("/user/login", userController.login));
app.use(router.get("/user/logout", userController.logout));
app.use(router.get("/user/register", userController.register));
app.listen(3000);

Has built in router
Can have named, optional and repeating parameters
server.route({
// optionally we can respond
// to multiple http request types
method: ['GET'/* 'POST' */],
path: '/user/login',
handler: function (request, reply) {
reply('Hello!');
}
});

Named parameters
server.route({
// optionally we can respond
// to multiple http request types
method: ['GET'/* 'POST' */],
path: '/user/{name}',
handler: function (request, reply) {
// we can retrieve the name as follows
var name = request.params.name;
reply('Hello!');
}
});

Named and optional parameters
server.route({
// optionally we can respond
// to multiple http request types
method: ['GET'/* 'POST' */],
path: '/user/{name?}',
handler: function (request, reply) {
// we can retrieve the name as follows
var name = request.params.name || 'John Doe';
reply('Hello!');
}
});

Named and repeating parameters
server.route({
// optionally we can respond
// to multiple http request types
method: ['GET'/* 'POST' */],
path: '/user/{names*2}',
handler: function (request, reply) {
// for a request to /user/john/doe
// we can retrieve the names as follows
var first_name, last_name;
[first_name, last_name] = request.params.name.split("/");
reply("Hello ${first_name} ${last_name}!");
}
});
Topics
- History
- Numbers
- TL; DR
- Hello Worlds
- Routing
- Validating parameters

Best place to validate is within the app.param method
app.get("/user/:user_id", (req, res) => {/**/})
app.param("user_id", (req, res, next, user_id) =>{
if(user_id.match(/\D/g)){
return next(new Error("Only numbers allowed for user id");
}
next();
})

Validating with an external library
// npm install --save validator
var validator = require("validator");
app.get("/user/:user_id", (req, res) => {/**/})
app.param("user_id", (req, res, next, user_id) =>{
if(!validator.isInt(user_id)){
return next(new Error("Only numbers allowed for user id");
}
next();
})

Doesn't have a built in way to handle payload validation
You have to include a validation library

var koa = require('koa');
var app = koa();
app.use(require('koa-body')());
app.use(require('koa-validate')());
app.use(require('koa-router')(app));
app.post('/signup', function * () {
this.checkBody('name').optional().len(2, 20,"are you kidding me?");
this.checkBody('password').notEmpty().len(3, 20).md5();
//empty() mean this param can be a empty string.
this.checkBody('nick').optional().empty().len(3, 20);
yield this.checkFile('icon')
.notEmpty()
.size(0,300*1024,'file too large')
.move("/static/icon/" , function*(file,context){
//resize image
});
if (this.errors) {
this.body = this.errors;
return;
}
this.body = this.request.body;
});




var Joi = require('joi');
var schema = Joi.object().keys({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
access_token: [Joi.string(), Joi.number()],
birthyear: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email()
}).with('username', 'birthyear').without('password', 'access_token');


var Joi = require('joi');
var schema = Joi.object().keys({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
access_token: [Joi.string(), Joi.number()],
birthyear: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email()
}).with('username', 'birthyear').without('password', 'access_token');
What's this?


var Joi = require('joi');
var schema = Joi.object().keys({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
access_token: [Joi.string(), Joi.number()],
birthyear: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email()
}).with('username', 'birthyear').without('password', 'access_token');
What's this?


var Joi = require('joi');
var schema = Joi.object().keys({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
access_token: [Joi.string(), Joi.number()],
birthyear: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email()
}).with('username', 'birthyear').without('password', 'access_token');
What's this?


var Joi = require('joi');
var schema = Joi.object().keys({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
access_token: [Joi.string(), Joi.number()],
birthyear: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email()
}).with('username', 'birthyear').without('password', 'access_token');
What's this?
Username has to be paired with birthyear
Password must not appear together with access_token


What's this?
server.route({
method: 'GET',
path: '/hello/{name}',
handler: function (request, reply) {
reply('Hello ' + request.params.name + '!');
},
config: {
validate: {
params: {
name: Joi.string().min(3).max(10)
}
}
}
});
The SKG Addition
Features
- Declare routes in a json object
- Define all errors in one place
- Have your routes grouped
- Validate your parameters
- Easy to test with mock data
- And more
Express Happiness
Routes
routes: {
a: {
subRoutes: {
b: {
// here goes the GET /a/b route definition
get: { },
// here goes the POST /a/b route definition
post: { },
// here goes the GET /a/b/c route definition
subRoutes: {
c: { get: { } }
}
}
}
}
}
Express Happiness
Routes for /users/:id
{routes: {
users: {
get: {...},
subRoutes: {
":id": {
// get user's info
get: {...},
// create a new user
post: {...},
// create a new user
put: {...},
// create a new user
delete: {...},
}
}
}
}}
Express Happiness
Routes for /users/:id
get: {
alias: 'user_get',
description: "Get user's info",
fields: [
// id will be favidated as
// defined in our fields validation object
fieldsLoader.getField('id')
]
}
Express Happiness
Routes for /users/:id
fieldsLoader.getField('id')
param: {
"key": "id",
"type": "int",
"humanReadable": "User's id",
"description": "The users id as saved on the DB",
"mandatory": "true",
"min": 0,
"validationFailureTexts": {
"mandatory": "Please provide the user's id",
"min": "ID must be a positive number"
}
}
Express Happiness
Error handling
'404': {
log: true,
sendToClient: {
code: 404,
data: 'Invalid route'
}
}, 'my_custom_error_code': {
log: false,
sendToClient: {
code: 500,
data: 'There was an error fulfilling your request'
}
}
Express Happiness
Error handling / Triggering
var err = new Error();
err.type = 'my_custom_error';
err.details = 'The details of the error';
return next(err);
From any middleware we can trigger an error
Express Happiness
Mocking results
- Add the mock: true property on the route definition
- Create a file returning the mocked data named <route_alias>.json
HOW TO
Express Happiness
Routes for /users/:id | Mocking
get: {
alias: 'user_get',
description: "Get user's info",
fields: [
// id will be favidated as
// defined in our fields validation object
fieldsLoader.getField('id')
],
mock: true
}
Express Happiness
Routes for /users/:id | Mocking
// in mocksfolder/user_get.json
module.exports = {
first_name: "John",
last_name: "Doe",
age: 32
}
Express Happiness
Route groups
{routes: {
users: {
groups: ["authenticate_users"],
get: {...},
subRoutes: {
":id": {
// get user's info
get: {...},
// create a new user
post: {...},
// create a new user
put: {...},
// create a new user
delete: {...},
}
}
}
}}
Express Happiness
Route groups
var ex_happi = new expressHappiness(app, router, {
mockData:{
enable: true,
folder: 'mockdata',
global: true
},
reusableFieldsFile: 'reusableFields.js',
errorFile:'errors.log',
errorsConfigurationFile: 'conf/errors.js',
apiConfigurationFile: 'conf/restConf.js',
controllersFile: 'controllerFunctions.js'
});
Create express happiness app
Express Happiness
Route groups
var UserAuthenticator = require("./UserAuthenticator");
var ex_happi.generate("/api", {
// pre validation middlewares
"authenicate_users": [UserAuthenticator.authenticate]
}, {
// post validation middlewares
});
// UserAuthenticator.js
module.exports = {
authenticate: function(req, res, next){
// authenticate your user
}
}
Create express happiness app
Thank you

Node.js Web Frameworks
By antoniskam1
Node.js Web Frameworks
- 4,246