- By Me
Today
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"
}
}
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
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)
// 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);
});
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}!");
}
});
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)
}
}
}
});
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: { } }
}
}
}
}
}
{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: {...},
}
}
}
}}
get: {
alias: 'user_get',
description: "Get user's info",
fields: [
// id will be favidated as
// defined in our fields validation object
fieldsLoader.getField('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"
}
}
'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'
}
}
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
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
}
// in mocksfolder/user_get.json
module.exports = {
first_name: "John",
last_name: "Doe",
age: 32
}
{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: {...},
}
}
}
}}
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'
});
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
}
}