Categories
Fastify

Server-Side Development with Fastify — Reply Serializer, Route Prefix, and Not Found Handler

Fastify is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Fastify.

Close the Server

We can call fastify.close to close the server.

For example, we can write:

const fastify = require('fastify')()

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

fastify.ready(err => {
  if (err) throw err
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    await fastify.close()
    process.exit(1)
  }
}
start()

to call fastify.close without an argument, which will return a promise.

Route Prefix

We can add a route prefix to our tours with the prefix option.

For instance, we can write:

const fastify = require('fastify')()

fastify.register(function (instance, opts, done) {
  instance.get('/foo', function (request, reply) {
    request.log.info('prefix: %s', instance.prefix)
    reply.send({ prefix: instance.prefix })
  })

  instance.register(function (instance, opts, done) {
    instance.get('/bar', function (request, reply) {
      request.log.info('prefix: %s', instance.prefix)
      reply.send({ prefix: instance.prefix })
    })

    done()
  }, { prefix: '/v2' })

  done()
}, { prefix: '/v1' })

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

Then when we go to /v1/v2/bar , we see:

{
  "prefix": "/v1/v2"
}

returned. And if we go to /v1/foo , we see:

{
  "prefix": "/v1"
}

Set Reply Serializer

We can call the setReplySerializer method to set the reply serializer for all routes.

For instance, we can write:

const fastify = require('fastify')()

fastify.setReplySerializer(function (payload, statusCode){
  return `status: ${statusCode}, content: ${payload}`
})

fastify.get('/', async (request, reply) => {
  return 'hello world'
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    await fastify.close()
    process.exit(1)
  }
}
start()

to add the serializer into our app.

Set Not Found Handler

We can set a handler for handler 404 errors.

For example, we can write:

const fastify = require('fastify')()

fastify.setNotFoundHandler({
  preValidation: (req, reply, done) => {
    done()
  },
  preHandler: (req, reply, done) => {
    done()
  }
}, function (request, reply) {
    reply.send('not found')
})

fastify.get('/', async (request, reply) => {
  return 'hello world'
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    await fastify.close()
    process.exit(1)
  }
}
start()

to call setNotFoundHandler on the server instance.

We can also set the not found handler on a group of URLs.

For instance, we can write:

const fastify = require('fastify')()

fastify.register(function (instance, options, done) {
  instance.setNotFoundHandler(function (request, reply) {
    return reply.send('not found')
  })
  done()
}, { prefix: '/v1' })

fastify.get('/', async (request, reply) => {
  return 'hello world'
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    await fastify.close()
    process.exit(1)
  }
}
start()

to add a not found handler for the routes that starts with /v1 .

Conclusion

We can close the server, set route prefixes, set not found handlers, and set a reply serializer with Fastify.

Categories
Fastify

Server-Side Development with Fastify — Rewrite URL and Loading Events

Fastify is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Fastify.

Rewrite URL

We can rewrite URLs with the rewriteUrl option.

For example, we can write:

const fastify = require('fastify')({
  rewriteUrl (req) {
    return req.url === '/hi' ? '/hello' : req.url;
  }
})

fastify.get('/hello', (request, reply) => {
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

Then when we go to /hi , the /hello route is invoked.

Server Methods

fastify.server is the Node core server object returned by the Fastify factory function.

The after method lets us run code after current plugin and all the plugins registered within it have finished loading.

For example, we can write:

const fastify = require('fastify')()
fastify
  .register((instance, opts, done) => {
    console.log('Current plugin')
    done()
  })
  .after(err => {
    console.log('After current plugin')
  })
  .register((instance, opts, done) => {
    console.log('Next plugin')
    done()
  })
  .ready(err => {
    console.log('Everything has been loaded')
  })

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

Then we see:

Current plugin
After current plugin
Next plugin
Everything has been loaded

logged in the console.

If after is called without a function, then it returns a promise.

For instance, we can write:

(async ()=>{
  const fastify = require('fastify')()
  fastify.get('/', (request, reply) => {
    reply.send('hello world')
  })

  await fastify.register(async (instance, opts) => {
    console.log('Current plugin')
  })

  await fastify.after()
  console.log('After current plugin')

  await fastify.register(async (instance, opts) => {
    console.log('Next plugin')
  })

  await fastify.ready()

  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})()

We call fastify.register with an async function to load our plugins.

Then we call fastify.after with await to run code after the plugin is loaded.

We can call the fastify.ready to watch when tyhe server is ready to handle requests.

For example, we can write:

const fastify = require('fastify')()

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

fastify.ready(err => {
  if (err) throw err
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to add a callback to it.

We can also make it return a promise if we don’t pass in a callback:

const fastify = require('fastify')()

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

fastify.ready(err => {
  if (err) throw err
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
    await fastify.ready()
    console.log('successfully booted!')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

Listen to Requests

We can listen for requests with the fastify.listen method.

For example, we write:

const fastify = require('fastify')()

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

fastify.ready(err => {
  if (err) throw err
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to start listening to requests with fastify.listen .

The first argument is the port to listen to and the 2nd argument is the IP address to listen to.

Conclusion

We can rewrite URLs and listen to various events with Fastify.

Categories
Fastify

Server-Side Development with Fastify — Error Handler and Query String Options

Fastify is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Fastify.

Error Handler and Query String Options

Fastify has lots of server options we can configure.

The trustProxy option lets Fastify know that it’s sitting behind a proxy.

We can set it to an IP address of the proxy server or a boolean.

Also, we can create a function to set the return the proxy IPs to trust.

We can access the ip, ips, hostname and protocol values on the [request](https://medium.com/r/?url=https%3A%2F%2Fwww.fastify.io%2Fdocs%2Flatest%2FRequest) object:

let i = 0
const fastify = require('fastify')({
  genReqId (req) { return i++ }
})

fastify.get('/', (request, reply) => {
  console.log(request.ip)
  console.log(request.ips)
  console.log(request.hostname)
  console.log(request.protocol)
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

ip has the IP address. hostname has the hostname, protocol has the protocol.

The querystringParser option lets us set the query string parser we want to use.

For example, we can write:

const qs = require('qs')
const fastify = require('fastify')({
  querystringParser: str => qs.parse(str)
})
fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to use the qs.parse method to parse query strings to an object.

The versioning option lets us version our routes with semver versioning.

We can set this option by writing:

const versioning = {
  storage () {
    let versions = {}
    return {
      get: (version) => { return versions[version] || null },
      set: (version, store) => { versions[version] = store },
      del: (version) => { delete versions[version] },
      empty: () => { versions = {} }
    }
  },
  deriveVersion: (req, ctx) => {
    return req.headers['accept']
  }
}

const fastify = require('fastify')({
  versioning
})

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

We add the get , set , and del methods to add the manipulate the versions object to work with the versions.

frameworkErrors lets us provide error handlers for errors our app encounters.

For example, we can write:

const fastify = require('fastify')({
  frameworkErrors (error, req, res) {
    if (error instanceof FST_ERR_BAD_URL) {
      res.code(400)
      return res.send("Provided url is not valid")
    } else {
      res.send(err)
    }
  }
})

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

We add our own handler for the bad URL error.

And we return a 400 error if we encounter it.

The clientErrorHandler lets us listen to error events emitted by the client connection and returns a 400 error.

For instance, we can write:

const fastify = require('fastify')({
  clientErrorHandler (err, socket) {
    const body = JSON.stringify({
      error: {
        message: 'Client error',
        code: '400'
      }
    })

this.log.trace({ err }, 'client error')
    socket.end(`HTTP/1.1 400 Bad RequestrnContent-Length: ${body.length}rnContent-Type: application/jsonrnrn${body}`)
  }
})

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to add our own clientErrorHandler to add client-side errors.

Conclusion

We can add various handlers to our Fasttify app.

Categories
Fastify

Server-Side Development with Fastify — Server Options

Fastify is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Fastify.

Server Options

We can set some options when we require fastify .

http2 lets us use HTTP/2 to bind the socket.

https lets ys listen for TLS connections.

connectionTimeout lets us define the server timeout in milliseconds.

keepAliveTimeout lets us define the server keep-alive timeout in milliseconds.

ignoreTrailingSlash makes Fastify ignore trailing slashes when matching routes.

For example, if we have:

const fastify = require('fastify')({
  ignoreTrailingSlash: true
})

fastify.get('/foo/', function(req, reply) {
  reply.send('foo')
})

// registers both "/bar" and "/bar/"
fastify.get('/bar', function(req, reply) {
  reply.send('bar')
})
const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

The /foo and /foo/ matches the /foo/ route.

And /bar and /bar/ matches the /bar/ route.

maxParamLength sets the max length of the route parameter. Anything longer than the length won’t be invoked.

bodyLimit sets the max request body size.

logger lets us enable the logger.

We can add pino as a custom logger by writing:

const pino = require('pino')();

const customLogger = {
  info(o, ...n) { },
  warn(o, ...n) { },
  error(o, ...n) { },
  fatal(o, ...n) { },
  trace(o, ...n) { },
  debug(o, ...n) { },
  child() {
    const child = Object.create(this);
    child.pino = pino.child(...arguments);
    return child;
  },
};

const fastify = require('fastify')({
  logger: customLogger
})

fastify.get('/foo/', function(req, reply) {
  reply.send('foo')
})

// registers both "/bar" and "/bar/"
fastify.get('/bar', function(req, reply) {
  reply.send('bar')
})
const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

disableRequestLogging lets us disable logging of requests.

serverFactory lets us pass a custom HTTP server to Fastify.

For example, we can write:

const Fastify = require('fastify')
const http = require('http')

const serverFactory = (handler, opts) => {
  const server = http.createServer((req, res) => {
    handler(req, res)
  })
  return server
}

const fastify = Fastify({ serverFactory })

fastify.get('/', (req, reply) => {
  reply.send({ hello: 'world' })
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to create a server with the http module.

caseSensitive lets us set whether to match routes in a case sensitive manner.

For example, if we have:

const fastify = require('fastify')({
  caseSensitive: false
})

fastify.get('/user/:username', (request, reply) => {
  reply.send(request.params.username)
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

Then if we go to /USER/NODEJS , we see NODEJS since caseSensitive is false .

The genReqId option lets us generate the request ID with our own function,.

For example, we can write:

let i = 0
const fastify = require('fastify')({
  genReqId (req) { return i++ }
})

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to create our own function to generate the request ID.

Conclusion

We can configure a few options with our Fastify server.

Categories
Fastify

Getting Started with Server-Side Development with Fastify

Fastify is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Fastify.

Installation

We install the fastify package by running:

npm i fastify --save

with NPM or:

yarn add fastify

with Yarn.

First App

We create a simple server app with a few lines of code.

For example, we can write:

const fastify = require('fastify')({
  logger: true
})

fastify.get('/', (request, reply) => {
  reply.send({ hello: 'world' })
})

fastify.listen(3000, '0.0.0.0', (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info(`server listening on ${address}`)
})

to create a simple server with the / route.

logger set to true enables the built-in logger.

reply.send sends a response.

fastify.listen starts the app and listens to the server.

The first argument is the port, the 2nd argument is the IP address to listen for requests from. '0.0.0.0' listens to all addresses.

When we go to / , we see:

{"hello":"world"}

returned.

We can also use astbc and await in our router handlers.

For example, we can write:

const fastify = require('fastify')({
  logger: true
})

fastify.get('/', async (request, reply) => {
  return { hello: 'world' }
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

We just return the object we want as the response.

And we call fastify.listen without the callback.

Validate Request Data

We can validate request data by passing in an option to our route.

For example, we can write:

const fastify = require('fastify')({
  logger: true
})

const opts = {
  schema: {
    body: {
      type: 'object',
      properties: {
        foo: { type: 'string' },
        bar: { type: 'number' }
      }
    }
  }
}

fastify.post('/', opts, async (request, reply) => {
  return { hello: 'world' }
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to create our schema and then pass in the properties into it.

We specify the types of the foo and bar properties.

This lets us specify the properties that are accepted.

Serializing Data

We can also specify the schema for the response.

For example, we can write:

const fastify = require('fastify')({
  logger: true
})

const opts = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

fastify.post('/', opts, async (request, reply) => {
  return { hello: 'world' }
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to specify the response that’s returned by the / route.

Run Our Server

We can run our Fastify app with the Fastify CLI.

We install it by running:

npm i fastify-cli

Then in package.json , we add:

{
  "scripts": {
    "start": "fastify start server.js"
  }
}

Then we run it with:

npm start

Conclusion

We can create a simple back end Node web app easily with Fastify.