/fastify-notes

Fastify Notes for Quick Start

Primary LanguageJavaScript

Main vscode plugins

  • JavaScript Pack

  • node-snippets

  • Node.js Extension Pack

  • Fastify Snippets

  • Bracket Pair Colorizer

  • Fastify code snippets

  • Node.js Modules Intellisense


Creating starters

express

npm i -g express-generator@4 
express --hbs express-web-server

fastify

npm i fastify-cli -g #install cli
#or
npm init fastify  #bootstrap project

npm install

or

fastify generate . --integrate #(if package.json already exists)
#or
npm init fastify -- --integrate #(if package.json already exists)

npm install

Main Fastify Plugins

npm i --save fastify-static
npm i --save point-of-view 
npm i --save got
npm i fastify-reply-from
npm i fastify-http-proxy fastify
npm i --save-dev fastify-static

Serving web content

  • fastify plugin (fastify-static)

we can then configure fastify static in the app.js file. Here we define our static folder to be public

const fastifyStatic = dev && require('fastify-static')

if (dev) {
    fastify.register(fastifyStatic, {
      root: path.join(__dirname, 'public')
    })
  }

we can create static html files in public folder, and if we link to other files, we have to add the .html extension

<a href='/hello.html'>Hello</a>

However, if we want to alias hello to hello.html we can create a hello folder in routes, with an index.js file that defines "sendFile" for that route '/hello'

module.exports = async (fastify, opts) => {
  fastify.get('/', async (request, reply) => {
    return reply.sendFile('hello.html')
  })
}

It will now to serve it from public bcz we've defined public there in app.js


Using Templates with Fastify

install dependencies and view rendering plugin

npm install point-of-view handlebars

then in app.js

const pointOfView = require('point-of-view')
const handlebars = require('handlebars')

fastify.register(pointOfView, {
    engine: { handlebars },
    root: path.join(__dirname, 'views'),
    layout: 'layout.hbs'
  })

we can then create templates in the 'views' folder and put our handlebars

in routes/root.js we need to set a file to render the index.hbs from the views folder

module.exports = async (fastify, opts) => {
  fastify.get('/', async (request, reply) => {
    return reply.view('index.hbs')
  })
}

We can pass any template parameters in the second param of reply.view


consuming other services

you can use got package

npm install got

code sample here https://github.com/abbathaw/fastify-got


proxying requests (single route, multi origin proxy)

use fastify plugin

const replyFrom = require('fastify-reply-from')
fastify.register(replyFrom)

In code then you can do sth like this

'use strict'

const { Readable } = require('stream')
async function * upper (res) {
  for await (const chunk of res) {
    yield chunk.toString().toUpperCase()
  }
}

module.exports = async function (fastify, opts) {
  const { httpErrors } = fastify
  fastify.get('/', async function (request, reply) {
    const {url } = request.query
    if (!url) {
      throw httpErrors.badRequest();
    }
    try {
      new URL(url)
    } catch (e) {
      throw httpErrors.badRequest();
    }
    return reply.from(url)
    
    //modifying the reply to uppercase
    // return reply.from(url, {
    //   onResponse (request, reply, res) {
    //     reply.send(Readable.from(upper(res)))
    //   }
    // })
    
  })
}

full proxy example (single origin, multi route proxy)

  • npm install fastify-http-proxy

then in app.js

'use strict'
const proxy = require('fastify-http-proxy')
module.exports = async function (fastify, opts) {
  fastify.register(proxy, {
    upstream: 'htt‌ps://news.ycombinator.com/'
  })
}
  • adding a prehandler to the proxy (with fastify-sensible)
const proxy = require('fastify-http-proxy')
const sensible = require('fastify-sensible')
module.exports = async function (fastify, opts) {
  fastify.register(sensible)
  fastify.register(proxy, {
    upstream: 'https://news.ycombinator.com/',
    async preHandler(request, reply) {
      if (request.query.token !== 'abc') {
        throw fastify.httpErrors.unauthorized()
      }
    }
  })
}

preventing parameter pollution (lab)

  • question: This is a small Express service that uppercases any input sent via a ​un​ query string parameter,but it waits one second before sending the response.This service is vulnerable to parameter pollution. A URL such ashttp://localhost:3000/?un=a&un=b​ will cause the service to crash, assuming the service islistening on port 3000.Fix it, without changing any of the current functionality.
'use strict'
const express = require('express')
const app = express()
const router = express.Router()
const { PORT = 3000 } = process.env

router.get('/', (req, res, next) => {
  
  if (!req.query.un) {
    var error = new Error('Bad Request')
    error.status = 400
    next(error);
    return
  }
  if (Array.isArray(req.query.un)) {
    console.log("what is", req.query.un)
    res.send(req.query.un.map(o=> {
    setTimeout(() => {
        return (o || '').toUpperCase()
      }, 1000)
    }))
  } else {
    setTimeout(() => {
      res.send((req.query.un || '').toUpperCase())
    }, 1000)
  }
})

app.use(router)

Validation and Response

//Validate the Post
{
    data: {
        brand: "samsung",
        color: "black"
    }
}

const postOptions = {
  schema: {
    body: {
      type: "object",
      required: ["data"],
      additionalProperties: false,
      properties: {
        data: {
          type: "object",
          required: ["brand", "color"],
          additionalProperties: false,
          properties: {
            brand: { type: "string" },
            color: { type: "string" },
          },
        },
      },
    },
  },
};

//Validate

{
  brand: "samsung",
  color: "black"
}


  const postOptions2 = {
    schema: {
      body: {
        type: "object",
        required: ["brand", "color"],
        additionalProperties: false,
        properties: {
          brand: { type: "string" },
          color: { type: "string" },
        },
      },
    },
  };
//Validate the Querystring

http://www.test.com?name=test&excitement=1

const queryOptions = {
  schema: {
    querystring: {
      type: "object",
      required: ["name", "excitement"],
      properties: {
        name: { type: "string" },
        excitement: { type: "integer" },
      },
    },
  },
};

Create Fastify Module

'use strict' 
/**
 *
 * @param {import('fastify').FastifyInstance} fastify
 */
 module.exports = async function(fastify, opts, next) { 
    
 }

//Deny Ip 211.133.33.11
const fp = require("fastify-plugin");

module.exports = fp(async function (fastify, opts) {
  const { forbidden } = fastify.httpErrors;
  fastify.addHook("onRequest", function hook(request, reply, done) {
    
    if (request.ip === "211.133.33.113") {
      throw forbidden();
    }
    done();
  });
});

Fastify Sensible

const { badRequest, forbidden, notFound, methodNotAllowed, internalServerError  } = fastify.httpErrors;

Great Fastify Link

https://github.com/hidjou/classsed-fastify-tutorial