Take your HTTP server to


by @matteocollina

Node Core

'use strict'

var server = require('http').createServer(handle)

server.listen(3000)

function handle (req, res) {
  res.setHeader('Content-Type', 'application/json')
  res.end(JSON.stringify({ hello: 'world' }))
}

Express

'use strict'

var express = require('express')
var app = express()
app.disable('etag').disable('x-powered-by')

app.get('/', function (req, res) {
  res.json({ hello: 'world' })
})

app.listen(3000)

The path to fast

  1. Start from scratch

  2. Add a feature

  3. Measure

  4. Optimize until it has no overhead

  5. GOTO 2

Can we speed up Core?

The problem with JSON.stringify

Serialization

fast-json-stringify

require('http').createServer(handle).listen(3000)
var flatstr = require('flatstr')

var stringify = require('fast-json-stringify')({
  type: 'object',
  properties: { hello: { type: 'string' } }
})

function handle (req, res) {
  res.setHeader('Content-Type', 'application/json')
  res.end(flatstr(stringify({ hello: 'world' })))
}

Adding an HTTP router

routing

https://github.com/delvedor/router-benchmark
const router = require('find-my-way')()
const flatstr = require('flatstr')
require('http').createServer(router.lookup.bind(router)).listen(3000)

var stringify = require('fast-json-stringify')({
  type: 'object',
  properties: { hello: { type: 'string' } }
})
router.on('GET', '/', function root (req, res, params) {
  res.setHeader('Content-Type', 'application/json')
  res.end(flatstr(stringify({ hello: 'world' })))
}

https://github.com/fastify/fastify

'use strict'
const fastify = require('fastify')()

fastify.get('/', async function (req, reply) {
  return { hello: 'world' }
})

fastify.listen(3000)
fastify.get('/', {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: { hello: { type: 'string' } }
      }
    }
  }
}, (req, reply) => {
  reply.send({ hello: 'world' })
})

Features Comparison

Feature Express Hapi Fastify
router
middleware
plugins
validation
hooks
decorators
logging
async/await
req/sec 18k 20k 36k

The problem with closures

function process (bigdata, cb) {
  remoteCall(bigdata, function (err, something) {
    storeSomething(something, function (err, res) {
      // this function is short-lived and hard to optimize
      // bigdata is still in scope!
      cb(null, res * 2)
    })
  })
}

Avoid nested closures 1/2

function process (bigdata, cb) {
  remoteCall(bigdata, function (err, something) {
    // bigdata exits scope here
    callStoreSomething(something, cb)
  })
}

Avoid nested closures 2/2

function callStoreSomething(something, cb) {
  /* this function can be optimized! */
  storeSomething(something, function (err, res) {
    cb(null, res * 2)
  })
}

Fastify is extremely careful in maintaining the least amount of state in the most efficient way.

middleware

fastify consumes (req, res, next) middlewares!

helmet comparison

validation speed


https://github.com/ebdrup/json-schema-benchmark

validation correctness


https://github.com/ebdrup/json-schema-benchmark

Fast is not everything

Fastify plugin system

fastify.register(
  require('my-plugin'),
  { options }
)

Everything that could be a plugin, should be a plugin

module.exports = function myPlugin (fastify, opts, next) {
  // register other plugins
  fastify.register(...)
  // add hooks
  fastify.addHook(...)
  // add decorator
  fastify.decorate(...)
  // add routes
  fastify.route(...)

  next()
}
const fp = require('fastify-plugin')

async function myPlugin (fastify, options) {
  fastify.decorate('util', yourAwesomeUtility)
  // now you can use it with `fastify.util`
}

module.exports = fp(myPlugin)
const fastify = require('fastify')()

fastify.register(require('./api/v1'), {
  prefix: '/v1',
  logLevel: 'error'
})

fastify.register(require('./api/v2'), {
  prefix: '/v2',
  logLevel: 'debug'
})

https://www.fastify.io

Demo

Fastify is not built by one developer

Open Open Source

Individuals making significant and valuable *contributions* are given commit-access to the project to contribute as they see fit.

openopensource.org

10 Collaborators

110 Contributors

>= 1800 Commits

>= 98 versions

87 plugins

Most code does not need to go

This presentation

Thanks!


matteo.collina@nearform.com

@matteocollina on Twitter

www.nearform.com