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 stringify = require('fast-json-stringify')({
  type: 'object',
  properties: { hello: { type: 'string' } }
})

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

Adding an HTTP router

routing

https://github.com/delvedor/router-benchmark
const router = require('find-my-way')()
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(stringify({ hello: 'world' }))
}

https://github.com/fastify/fastify

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

fastify.get('/', function (req, reply) {
  reply.send({ 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 20860 5768 34613

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: no closures at all!

https://github.com/mcollina/reusify

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

https://www.fastify.io

Most code does not need to go

https://www.nearform.com/blog/node-js-is-getting-a-new-v8-with-turbofan/

Acknowledgements





This presentation

http://github.com/mcollina

Thanks!


If you need help with Node.js, contact us


matteo.collina@nearform.com

@matteocollina on Twitter

www.nearform.com