Upgrade to Pro — share decks privately, control downloads, hide ads and more …

When your server is slow, call Fastify to the rescue

When your server is slow, call Fastify to the rescue

Tamar Twena-Stern

October 27, 2023
Tweet

More Decks by Tamar Twena-Stern

Other Decks in Technology

Transcript

  1. I am going to develop a shiny and new micro

    service , which Node.js Framework should I choose ?
  2. Don’t you think it is time we will all try

    something new, which might be more updated for today’s standards ?
  3. A web framework highly focused on providing the best developer

    experience with the least overhead and a powerful plugin architecture. It is inspired by Hapi and Express and as far as we know, it is one of the fastest web frameworks in town.
  4. Setup - Express Server import express from 'express'; const app

    = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello World!'); }) app.listen(port, () => { console.log(`Example app listening on port ${port}`); })
  5. Setup - Fastify Server import Fastify from 'fastify' const fastify

    = Fastify({ logger: false }); fastify.get('/', async (request, reply) => { return { hello: 'world' } });
  6. const start = async () => { try { await

    fastify.listen({ port: 3000 }); } catch (err) { fastify.log.error(err); process.exit(1); } } start(); Setup - Fastify Server
  7. Benchmark Execution With Autocannon autocannon -c 10 -m 'POST' -b

    '{ "animal" : "cat"}' -H 'Content-Type=application/json' http://localhost:3000/animals
  8. Setup - Express Server import express from 'express'; import mongoose

    from 'mongoose'; import bodyParser from 'body-parser'; import animalController from './animals.js'; const port = process.env.PORT || 3000; mongoose.connect('mongodb://localhost:27017/test_db); const app = express(); app.use(bodyParser.urlencoded({ extended: true }));
  9. Setup - Express Server - Continue const router = express.Router();

    router.post('/animals', async (req, res) => { await animalController(); return res.json({ "msg" : "OK" }); }); app.use('/', router); app.listen(port); console.log('server is up ' + port);
  10. Setup - Express Controller import Animal from'./animalModel.js'; const createAnimal =

    async () => { const animal = new Animal(); animal.animal = 'cat'; await animal.save(); }; export default createAnimal;
  11. Setup - Fastify Server const fastify = Fastify({ logger: false

    }); fastify.register(dbConnector) fastify.register(routes) fastify.listen({ port: 3000 }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } });
  12. Setup - Fastify Routes async function routes (fastify, options, done)

    { const collection=fastify.mongo.db.collection('test_collection'); const animalBodyJsonSchema = { type: 'object', required: ['animal'], properties: { animal: { type: 'string' }, }, }
  13. Setup - Fastify Routes - Continue const schema = {

    body: animalBodyJsonSchema, } fastify.post('/animals', { schema }, async (request, reply) => { const result = await collection.insertOne({ animal: request.body.animal }); return result }); //Function end export default routes
  14. Comparing Express Fastify Req/Sec, 97.5% 6691 9079 Total number of

    requests 70,000 86,000 Bytes Read/Sec 1.6MB 2.068MB
  15. Last Note • Mongoose also creates performance overhead • Observing

    a server with express and native Mongo driver
  16. Comparing Express Fastify Req/Sec, 97.5% 7047 9079 Total number of

    requests 74,000 86,000 Bytes Read/Sec 1.73MB 2.068MB
  17. Hello World With Fastify import Fastify from 'fastify' const fastify

    = Fastify({ logger: true }); fastify.get('/', async () => { return { hello: 'world' } });
  18. Hello World With Fastify import Fastify from 'fastify' const fastify

    = Fastify({ logger: true }); fastify.get('/', async () => { return { hello: 'world' } });
  19. The Pugin API async function routes (fastify, options, done) {

    const collection = fastify.mongo.db.collection('test_collection'); const animalBodyJsonSchema = { type: 'object', required: ['animal'], properties: { animal: { type: 'string' }, }, } const schema = { body: animalBodyJsonSchema, }
  20. The Pugin API fastify.post('/animals', { schema }, async (request, reply)

    => { const result = await collection.insertOne({ animal: request.body.animal }) return result }); done(); } export default routes;
  21. The Pugin API fastify.get('/animals/:animal', async (request, reply) => { const

    result = await collection.findOne({ animal: request.params.animal }); if (!result) { throw new Error('Invalid value'); } return result }); done(); } export default routes
  22. Registering A Plugin const fastify = Fastify({ logger: true });

    fastify.register(dbConnector) fastify.register(routes)
  23. Adding Functionality To A Fastify Scope With Decorators fastify.register((instance, opts,

    done) => { instance.decorate('util', (a, b) => a + b) console.log(instance.util('hello ', 'world')) done(); }); fastify.register((instance, opts, done) => { console.log(instance.util(‘hello ', 'world')) // This will throw an error done(); })
  24. Fastify Hooks fastify.decorate('util', (request, key, value) => { request[key] =

    value }) fastify.addHook('preHandler', (request, reply, done) => { fastify.util(request, 'timestamp', new Date()) done(); }); fastify.get('/plugin1', (request, reply) => { reply.send(request); }); fastify.get('/plugin2', (request, reply) => { reply.send(request); });
  25. Given Common Functionalities When Developing A Micro Service - What

    Will be The Best Fastify Approach For Each ?
  26. Answer : Fastify Hooks fastify.addHook('preHandler', (request, reply, done) => {

    if (request.body) { request.log.info({ body: request.body }, 'parsed body') } done(); });
  27. Can Be Done With A Hook On The Global Scope

    + Register A Plugin For Each Route
  28. async function routes (fastify, options, done) { const collection=fastify.mongo.db.collection('test_collection'); const

    animalBodyJsonSchema = { type: 'object', required: ['animal'], properties: { animal: { type: 'string' }, }, } Fastify Input Sanitizing Using Options
  29. const schema = { body: animalBodyJsonSchema, } fastify.post('/animals', { schema

    }, async (request, reply) => { const result = await collection.insertOne({ animal: request.body.animal }); return result }); //Function end export default routes Fastify Built In Input Sanitizing Using Options
  30. Answer : Fastify Plugin On Global Scope import cors from

    '@fastify/cors' const fastify = Fastify() await fastify.register(cors, { // put your options here });
  31. Answer 2 - Register 2 Different Plugins For Fastify Instance

    fastify.register(firstRouteGroup); fastify.register(SecondRouteGroup);
  32. • AWS Lambda: ◦ Write a fastify server ◦ Expose

    it in the lambda with @fastify/aws-lambda • Google cloud functions : ◦ Native support except content type parsing Serverless
  33. Summary When you choose a technology in an organization, you

    usually avoid 2 technologies. You are concerned of the overhead. Web server layer is usually infrastructure that will have less changes and maintenance . To my opinion you can have 2 technologies in that layer. Fastify is a good choice to consider.