Skip to content

Latest commit

 

History

History
2033 lines (1629 loc) · 63.5 KB

Server.md

File metadata and controls

2033 lines (1629 loc) · 63.5 KB

Fastify

Factory

The Fastify module exports a factory function that is used to create new Fastify server instances. This factory function accepts an options object which is used to customize the resulting instance. This document describes the properties available in that options object.

http

  • Default: null

An object used to configure the server's listening socket. The options are the same as the Node.js core createServer method.

This option is ignored if options http2 or https are set.

http2

  • Default: false

If true Node.js core's HTTP/2 module is used for binding the socket.

https

  • Default: null

An object used to configure the server's listening socket for TLS. The options are the same as the Node.js core createServer method. When this property is null, the socket will not be configured for TLS.

This option also applies when the http2 option is set.

connectionTimeout

  • Default: 0 (no timeout)

Defines the server timeout in milliseconds. See documentation for server.timeout property to understand the effect of this option.

When serverFactory option is specified this option is ignored.

keepAliveTimeout

  • Default: 72000 (72 seconds)

Defines the server keep-alive timeout in milliseconds. See documentation for server.keepAliveTimeout property to understand the effect of this option. This option only applies when HTTP/1 is in use.

When serverFactory option is specified this option is ignored.

forceCloseConnections

  • Default: "idle" if the HTTP server allows it, false otherwise

When set to true, upon close the server will iterate the current persistent connections and destroy their sockets.

Warning Connections are not inspected to determine if requests have been completed.

Fastify will prefer the HTTP server's closeAllConnections method if supported, otherwise, it will use internal connection tracking.

When set to "idle", upon close the server will iterate the current persistent connections which are not sending a request or waiting for a response and destroy their sockets. The value is only supported if the HTTP server supports the closeIdleConnections method, otherwise attempting to set it will throw an exception.

maxRequestsPerSocket

  • Default: 0 (no limit)

Defines the maximum number of requests a socket can handle before closing keep alive connection. See server.maxRequestsPerSocket property to understand the effect of this option. This option only applies when HTTP/1.1 is in use. Also, when serverFactory option is specified, this option is ignored.

Note At the time of writing, only node >= v16.10.0 supports this option.

requestTimeout

  • Default: 0 (no limit)

Defines the maximum number of milliseconds for receiving the entire request from the client. See server.requestTimeout property to understand the effect of this option.

When serverFactory option is specified, this option is ignored. It must be set to a non-zero value (e.g. 120 seconds) to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front.

Note At the time of writing, only node >= v14.11.0 supports this option

ignoreTrailingSlash

  • Default: false

Fastify uses find-my-way to handle routing. By default, Fastify will take into account the trailing slashes. Paths like /foo and /foo/ are treated as different paths. If you want to change this, set this flag to true. That way, both /foo and /foo/ will point to the same route. This option applies to all route registrations for the resulting server instance.

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

// registers both "/foo" and "/foo/"
fastify.get('/foo/', function (req, reply) {
  reply.send('foo')
})

// registers both "/bar" and "/bar/"
fastify.get('/bar', function (req, reply) {
  reply.send('bar')
})

ignoreDuplicateSlashes

  • Default: false

Fastify uses find-my-way to handle routing. You can use ignoreDuplicateSlashes option to remove duplicate slashes from the path. It removes duplicate slashes in the route path and the request URL. This option applies to all route registrations for the resulting server instance.

When ignoreTrailingSlash and ignoreDuplicateSlashes are both set to true Fastify will remove duplicate slashes, and then trailing slashes, meaning //a//b//c// will be converted to /a/b/c.

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

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

maxParamLength

  • Default: 100

You can set a custom length for parameters in parametric (standard, regex, and multi) routes by using maxParamLength option; the default value is 100 characters. If the maximum length limit is reached, the not found route will be invoked.

This can be useful especially if you have a regex-based route, protecting you against ReDoS attacks.

bodyLimit

  • Default: 1048576 (1MiB)

Defines the maximum payload, in bytes, the server is allowed to accept. The default body reader sends FST_ERR_CTP_BODY_TOO_LARGE reply, if the size of the body exceeds this limit. If preParsing hook is provided, this limit is applied to the size of the stream the hook returns (i.e. the size of "decoded" body).

onProtoPoisoning

  • Default: 'error'

Defines what action the framework must take when parsing a JSON object with __proto__. This functionality is provided by secure-json-parse. See Prototype Poisoning for more details about prototype poisoning attacks.

Possible values are 'error', 'remove', or 'ignore'.

onConstructorPoisoning

  • Default: 'error'

Defines what action the framework must take when parsing a JSON object with constructor. This functionality is provided by secure-json-parse. See Prototype Poisoning for more details about prototype poisoning attacks.

Possible values are 'error', 'remove', or 'ignore'.

logger

Fastify includes built-in logging via the Pino logger. This property is used to configure the internal logger instance.

The possible values this property may have are:

  • Default: false. The logger is disabled. All logging methods will point to a null logger abstract-logging instance.

  • object: a standard Pino options object. This will be passed directly to the Pino constructor. If the following properties are not present on the object, they will be added accordingly:

    • level: the minimum logging level. If not set, it will be set to 'info'.
    • serializers: a hash of serialization functions. By default, serializers are added for req (incoming request objects), res (outgoing response objects), and err (standard Error objects). When a log method receives an object with any of these properties then the respective serializer will be used for that property. For example:
      fastify.get('/foo', function (req, res) {
        req.log.info({req}) // log the serialized request object
        res.send('foo')
      })
      Any user-supplied serializer will override the default serializer of the corresponding property.

loggerInstance

  • Default: null

A custom logger instance. The logger must be a Pino instance or conform to the Pino interface by having the following methods: info, error, debug, fatal, warn, trace, child. For example:

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

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

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

disableRequestLogging

  • Default: false

When logging is enabled, Fastify will issue an info level log message when a request is received and when the response for that request has been sent. By setting this option to true, these log messages will be disabled. This allows for more flexible request start and end logging by attaching custom onRequest and onResponse hooks.

The other log entries that will be disabled are:

  • an error log written by the default onResponse hook on reply callback errors
  • the error and info logs written by the defaultErrorHandler on error management
  • the info log written by the fourOhFour handler when a non existent route is requested

Other log messages emitted by Fastify will stay enabled, like deprecation warnings and messages emitted when requests are received while the server is closing.

// Examples of hooks to replicate the disabled functionality.
fastify.addHook('onRequest', (req, reply, done) => {
  req.log.info({ url: req.raw.url, id: req.id }, 'received request')
  done()
})

fastify.addHook('onResponse', (req, reply, done) => {
  req.log.info({ url: req.raw.originalUrl, statusCode: reply.raw.statusCode }, 'request completed')
  done()
})

serverFactory

You can pass a custom HTTP server to Fastify by using the serverFactory option.

serverFactory is a function that takes a handler parameter, which takes the request and response objects as parameters, and an options object, which is the same you have passed to Fastify.

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' })
})

fastify.listen({ port: 3000 })

Internally Fastify uses the API of Node core HTTP server, so if you are using a custom server you must be sure to have the same API exposed. If not, you can enhance the server instance inside the serverFactory function before the return statement.

caseSensitive

  • Default: true

When true routes are registered as case-sensitive. That is, /foo is not equal to /Foo. When false then routes are case-insensitive.

Please note that setting this option to false goes against RFC3986.

By setting caseSensitive to false, all paths will be matched as lowercase, but the route parameters or wildcards will maintain their original letter casing. This option does not affect query strings, please refer to querystringParser to change their handling.

fastify.get('/user/:username', (request, reply) => {
  // Given the URL: /USER/NodeJS
  console.log(request.params.username) // -> 'NodeJS'
})

allowUnsafeRegex

  • Default false

Disabled by default, so routes only allow safe regular expressions. To use unsafe expressions, set allowUnsafeRegex to true.

fastify.get('/user/:id(^([0-9]+){4}$)', (request, reply) => {
  // Throws an error without allowUnsafeRegex = true
})

requestIdHeader

  • Default: 'request-id'

The header name used to set the request-id. See the request-id section. Setting requestIdHeader to true will set the requestIdHeader to "request-id". Setting requestIdHeader to a non-empty string will use the specified string as the requestIdHeader. By default requestIdHeader is set to false and will immediately use genReqId. Setting requestIdHeader to an empty String ("") will set the requestIdHeader to false.

  • Default: false
const fastify = require('fastify')({
  requestIdHeader: 'x-custom-id', // -> use 'X-Custom-Id' header if available
  //requestIdHeader: false, // -> always use genReqId
})

requestIdLogLabel

  • Default: 'reqId'

Defines the label used for the request identifier when logging the request.

genReqId

  • Default: value of 'request-id' header if provided or monotonically increasing integers

Function for generating the request-id. It will receive the raw incoming request as a parameter. This function is expected to be error-free.

Especially in distributed systems, you may want to override the default ID generation behavior as shown below. For generating UUIDs you may want to check out hyperid.

Note genReqId will be not called if the header set in requestIdHeader is available (defaults to 'request-id').

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

trustProxy

  • Default: false
  • true/false: Trust all proxies (true) or do not trust any proxies (false).
  • string: Trust only given IP/CIDR (e.g. '127.0.0.1'). May be a list of comma separated values (e.g. '127.0.0.1,192.168.1.1/24').
  • Array<string>: Trust only given IP/CIDR list (e.g. ['127.0.0.1']).
  • number: Trust the nth hop from the front-facing proxy server as the client.
  • Function: Custom trust function that takes address as first argument
    function myTrustFn(address, hop) {
      return address === '1.2.3.4' || hop === 1
    }

By enabling the trustProxy option, Fastify will know that it is sitting behind a proxy and that the X-Forwarded-* header fields may be trusted, which otherwise may be easily spoofed.

const fastify = Fastify({ trustProxy: true })

For more examples, refer to the proxy-addr package.

You may access the ip, ips, host and protocol values on the request object.

fastify.get('/', (request, reply) => {
  console.log(request.ip)
  console.log(request.ips)
  console.log(request.host)
  console.log(request.protocol)
})

Note If a request contains multiple x-forwarded-host or x-forwarded-proto headers, it is only the last one that is used to derive request.hostname and request.protocol.

pluginTimeout

  • Default: 10000

The maximum amount of time in milliseconds in which a plugin can load. If not, ready will complete with an Error with code 'ERR_AVVIO_PLUGIN_TIMEOUT'. When set to 0, disables this check. This controls avvio 's timeout parameter.

querystringParser

The default query string parser that Fastify uses is the Node.js's core querystring module.

You can use this option to use a custom parser, such as qs.

If you only want the keys (and not the values) to be case insensitive we recommend using a custom parser to convert only the keys to lowercase.

const qs = require('qs')
const fastify = require('fastify')({
  querystringParser: str => qs.parse(str)
})

You can also use Fastify's default parser but change some handling behavior, like the example below for case insensitive keys and values:

const querystring = require('node:querystring')
const fastify = require('fastify')({
  querystringParser: str => querystring.parse(str.toLowerCase())
})

exposeHeadRoutes

  • Default: true

Automatically creates a sibling HEAD route for each GET route defined. If you want a custom HEAD handler without disabling this option, make sure to define it before the GET route.

constraints

Fastify's built-in route constraints are provided by find-my-way, which allows constraining routes by version or host. You can add new constraint strategies, or override the built-in strategies, by providing a constraints object with strategies for find-my-way. You can find more information on constraint strategies in the find-my-way documentation.

const customVersionStrategy = {
  storage: function () {
    const versions = {}
    return {
      get: (version) => { return versions[version] || null },
      set: (version, store) => { versions[version] = store }
    }
  },
  deriveVersion: (req, ctx) => {
    return req.headers['accept']
  }
}

const fastify = require('fastify')({
  constraints: {
    version: customVersionStrategy
  }
})

return503OnClosing

  • Default: true

Returns 503 after calling close server method. If false, the server routes the incoming request as usual.

ajv

Configure the Ajv v8 instance used by Fastify without providing a custom one. The default configuration is explained in the #schema-validator section.

const fastify = require('fastify')({
  ajv: {
    customOptions: {
      removeAdditional: 'all' // Refer to [ajv options](https://ajv.js.org/options.html#removeadditional)
    },
    plugins: [
      require('ajv-merge-patch'),
      [require('ajv-keywords'), 'instanceof']
      // Usage: [plugin, pluginOptions] - Plugin with options
      // Usage: plugin - Plugin without options
    ]
  }
})

serializerOpts

Customize the options of the default fast-json-stringify instance that serializes the response's payload:

const fastify = require('fastify')({
  serializerOpts: {
    rounding: 'ceil'
  }
})

http2SessionTimeout

  • Default: 72000

Set a default timeout to every incoming HTTP/2 session in milliseconds. The session will be closed on the timeout.

This option is needed to offer a graceful "close" experience when using HTTP/2. The low default has been chosen to mitigate denial of service attacks. When the server is behind a load balancer or can scale automatically this value can be increased to fit the use case. Node core defaults this to 0.

frameworkErrors

  • Default: null

Fastify provides default error handlers for the most common use cases. It is possible to override one or more of those handlers with custom code using this option.

Note Only FST_ERR_BAD_URL and FST_ERR_ASYNC_CONSTRAINT are implemented at present.

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

clientErrorHandler

Set a clientErrorHandler that listens to error events emitted by client connections and responds with a 400.

It is possible to override the default clientErrorHandler using this option.

  • Default:
function defaultClientErrorHandler (err, socket) {
  if (err.code === 'ECONNRESET') {
    return
  }

  const body = JSON.stringify({
    error: http.STATUS_CODES['400'],
    message: 'Client Error',
    statusCode: 400
  })
  this.log.trace({ err }, 'client error')

  if (socket.writable) {
    socket.end([
      'HTTP/1.1 400 Bad Request',
      `Content-Length: ${body.length}`,
      `Content-Type: application/json\r\n\r\n${body}`
    ].join('\r\n'))
  }
}

Note clientErrorHandler operates with raw sockets. The handler is expected to return a properly formed HTTP response that includes a status line, HTTP headers and a message body. Before attempting to write the socket, the handler should check if the socket is still writable as it may have already been destroyed.

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

    // `this` is bound to fastify instance
    this.log.trace({ err }, 'client error')

    // the handler is responsible for generating a valid HTTP response
    socket.end([
      'HTTP/1.1 400 Bad Request',
      `Content-Length: ${body.length}`,
      `Content-Type: application/json\r\n\r\n${body}`
    ].join('\r\n'))
  }
})

rewriteUrl

Set a sync callback function that must return a string that allows rewriting URLs. This is useful when you are behind a proxy that changes the URL. Rewriting a URL will modify the url property of the req object.

Note that rewriteUrl is called before routing, it is not encapsulated and it is an instance-wide configuration.

// @param {object} req The raw Node.js HTTP request, not the `FastifyRequest` object.
// @this Fastify The root Fastify instance (not an encapsulated instance).
// @returns {string} The path that the request should be mapped to.
function rewriteUrl (req) {
  if (req.url === '/hi') {
    this.log.debug({ originalUrl: req.url, url: '/hello' }, 'rewrite url');
    return '/hello'
  } else {
    return req.url;
  }
}

useSemicolonDelimiter

  • Default false

Fastify uses find-my-way which supports, separating the path and query string with a ; character (code 59), e.g. /dev;foo=bar. This decision originated from [delvedor/find-my-way#76] (delvedor/find-my-way#76). Thus, this option will support backwards compatiblilty for the need to split on ;. To enable support for splitting on ; set useSemicolonDelimiter to true.

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

fastify.get('/dev', async (request, reply) => {
  // An example request such as `/dev;foo=bar`
  // Will produce the following query params result `{ foo = 'bar' }`
  return request.query
})

Instance

Server Methods

server

fastify.server: The Node core server object as returned by the Fastify factory function.

Warning If utilized improperly, certain Fastify features could be disrupted. It is recommended to only use it for attaching listeners.

after

Invoked when the current plugin and all the plugins that have been registered within it have finished loading. It is always executed before the method fastify.ready.

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')
  })

In case after() is called without a function, it returns a Promise:

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

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

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

await fastify.ready()

console.log('Everything has been loaded')

ready

Function called when all the plugins have been loaded. It takes an error parameter if something went wrong.

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

If it is called without any arguments, it will return a Promise:

fastify.ready().then(() => {
  console.log('successfully booted!')
}, (err) => {
  console.log('an error happened', err)
})

listen

Starts the server and internally waits for the .ready() event. The signature is .listen([options][, callback]). Both the options object and the callback parameters extend the Node.js core options object. Thus, all core options are available with the following additional Fastify specific options:

listenTextResolver

Set an optional resolver for the text to log after server has been successfully started. It is possible to override the default Server listening at [address] log entry using this option.

server.listen({
  port: 9080,
  listenTextResolver: (address) => { return `Prometheus metrics server is listening at ${address}` }
})

By default, the server will listen on the address(es) resolved by localhost when no specific host is provided. If listening on any available interface is desired, then specifying 0.0.0.0 for the address will listen on all IPv4 addresses. The following table details the possible values for host when targeting localhost, and what the result of those values for host will be.

Host IPv4 IPv6
:: *
:: + ipv6Only 🚫
0.0.0.0 🚫
localhost
127.0.0.1 🚫
::1 🚫

* Using :: for the address will listen on all IPv6 addresses and, depending on OS, may also listen on all IPv4 addresses.

Be careful when deciding to listen on all interfaces; it comes with inherent security risks.

The default is to listen on port: 0 (which picks the first available open port) and host: 'localhost':

fastify.listen((err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

Specifying an address is also supported:

fastify.listen({ port: 3000, host: '127.0.0.1' }, (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

If no callback is provided a Promise is returned:

fastify.listen({ port: 3000 })
  .then((address) => console.log(`server listening on ${address}`))
  .catch(err => {
    console.log('Error starting server:', err)
    process.exit(1)
  })

When deploying to a Docker, and potentially other, containers, it is advisable to listen on 0.0.0.0 because they do not default to exposing mapped ports to localhost:

fastify.listen({ port: 3000, host: '0.0.0.0' }, (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

If the port is omitted (or is set to zero), a random available port is automatically chosen (available via fastify.server.address().port).

The default options of listen are:

fastify.listen({
  port: 0,
  host: 'localhost',
  exclusive: false,
  readableAll: false,
  writableAll: false,
  ipv6Only: false
}, (err) => {})

addresses

This method returns an array of addresses that the server is listening on. If you call it before listen() is called or after the close() function, it will return an empty array.

await fastify.listen({ port: 8080 })
const addresses = fastify.addresses()
// [
//   { port: 8080, family: 'IPv6', address: '::1' },
//   { port: 8080, family: 'IPv4', address: '127.0.0.1' }
// ]

Note that the array contains the fastify.server.address() too.

routing

Method to access the lookup method of the internal router and match the request to the appropriate handler:

fastify.routing(req, res)

route

Method to add routes to the server, it also has shorthand functions, check here.

hasRoute

Method to check if a route is already registered to the internal router. It expects an object as the payload. url and method are mandatory fields. It is possible to also specify constraints. The method returns true if the route is registered or false if not.

const routeExists = fastify.hasRoute({
  url: '/',
  method: 'GET',
  constraints: { version: '1.0.0' } // optional
})

if (routeExists === false) {
  // add route
}

findRoute

Method to retrieve a route already registered to the internal router. It expects an object as the payload. url and method are mandatory fields. It is possible to also specify constraints. The method returns a route object or null if the route cannot be found.

const route = fastify.findRoute({
  url: '/artists/:artistId',
  method: 'GET',
  constraints: { version: '1.0.0' } // optional
})

if (route !== null) {
  // perform some route checks
  console.log(route.params)   // `{artistId: ':artistId'}`
}

close

fastify.close(callback): call this function to close the server instance and run the 'onClose' hook.

Calling close will also cause the server to respond to every new incoming request with a 503 error and destroy that request. See return503OnClosing flags for changing this behavior.

If it is called without any arguments, it will return a Promise:

fastify.close().then(() => {
  console.log('successfully closed!')
}, (err) => {
  console.log('an error happened', err)
})

decorate*

Function useful if you need to decorate the fastify instance, Reply or Request, check here.

register

Fastify allows the user to extend its functionality with plugins. A plugin can be a set of routes, a server decorator, or whatever, check here.

addHook

Function to add a specific hook in the lifecycle of Fastify, check here.

prefix

The full path that will be prefixed to a route.

Example:

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

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

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

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

pluginName

Name of the current plugin. The root plugin is called 'fastify'. There are different ways to define a name (in order).

  1. If you use fastify-plugin the metadata name is used.
  2. If the exported plugin has the Symbol.for('fastify.display-name') property, then the value of that property is used. Example: pluginFn[Symbol.for('fastify.display-name')] = "Custom Name"
  3. If you module.exports a plugin the filename is used.
  4. If you use a regular function declaration the function name is used.

Fallback: The first two lines of your plugin will represent the plugin name. Newlines are replaced by --. This will help to identify the root cause when you deal with many plugins.

Warning If you have to deal with nested plugins, the name differs with the usage of the fastify-plugin because no new scope is created and therefore we have no place to attach contextual data. In that case, the plugin name will represent the boot order of all involved plugins in the format of fastify -> plugin-A -> plugin-B.

hasPlugin

Method to check if a specific plugin has been registered. Relies on the plugin metadata name. Returns true if the plugin is registered. Otherwise, returns false.

const fastify = require('fastify')()
fastify.register(require('@fastify/cookie'), {
  secret: 'my-secret',
  parseOptions: {}
})

fastify.ready(() => {
  fastify.hasPlugin('@fastify/cookie') // true
})

listeningOrigin

The current origin the server is listening to. For example, a TCP socket based server returns a base address like http://127.0.0.1:3000, and a Unix socket server will return the socket path, e.g. fastify.temp.sock.

log

The logger instance, check here.

version

Fastify version of the instance. Used for plugin support. See Plugins for information on how the version is used by plugins.

inject

Fake HTTP injection (for testing purposes) here.

addHttpMethod

Fastify supports the GET, HEAD, TRACE, DELETE, OPTIONS, PATCH, PUT and POST HTTP methods by default. The addHttpMethod method allows to add any non standard HTTP methods to the server that are supported by Node.js.

// Add a new HTTP method called 'MKCOL' that supports a request body
fastify.addHttpMethod('MKCOL', { hasBody: true,  })

// Add a new HTTP method called 'COPY' that does not support a request body
fastify.addHttpMethod('COPY')

After calling addHttpMethod, it is possible to use the route shorthand methods to define routes for the new HTTP method:

fastify.addHttpMethod('MKCOL', { hasBody: true })
fastify.mkcol('/', (req, reply) => {
  // Handle the 'MKCOL' request
})

addSchema

fastify.addSchema(schemaObj), adds a JSON schema to the Fastify instance. This allows you to reuse it everywhere in your application just by using the standard $ref keyword.

To learn more, read the Validation and Serialization documentation.

getSchemas

fastify.getSchemas(), returns a hash of all schemas added via .addSchema. The keys of the hash are the $ids of the JSON Schema provided.

getSchema

fastify.getSchema(id), return the JSON schema added with .addSchema and the matching id. It returns undefined if it is not found.

setReplySerializer

Set the reply serializer for all the routes. This will be used as default if a Reply.serializer(func) has not been set. The handler is fully encapsulated, so different plugins can set different error handlers. Note: the function parameter is called only for status 2xx. Check out the setErrorHandler for errors.

fastify.setReplySerializer(function (payload, statusCode){
  // serialize the payload with a sync function
  return `my serialized ${statusCode} content: ${payload}`
})

setValidatorCompiler

Set the schema validator compiler for all routes. See #schema-validator.

setSchemaErrorFormatter

Set the schema error formatter for all routes. See #error-handling.

setSerializerCompiler

Set the schema serializer compiler for all routes. See #schema-serializer.

Note setReplySerializer has priority if set!

validatorCompiler

This property can be used to get the schema validator. If not set, it will be null until the server starts, then it will be a function with the signature function ({ schema, method, url, httpPart }) that returns the input schema compiled to a function for validating data. The input schema can access all the shared schemas added with .addSchema function.

serializerCompiler

This property can be used to get the schema serializer. If not set, it will be null until the server starts, then it will be a function with the signature function ({ schema, method, url, httpPart }) that returns the input schema compiled to a function for validating data. The input schema can access all the shared schemas added with .addSchema function.

schemaErrorFormatter

This property can be used to set a function to format errors that happen while the validationCompiler fails to validate the schema. See #error-handling.

schemaController

This property can be used to fully manage:

  • bucket: where the schemas of your application will be stored
  • compilersFactory: what module must compile the JSON schemas

It can be useful when your schemas are stored in another data structure that is unknown to Fastify.

Another use case is to tweak all the schemas processing. Doing so it is possible to use Ajv v8 JTD or Standalone feature. To use such as JTD or the Standalone mode, refers to the @fastify/ajv-compiler documentation.

const fastify = Fastify({
  schemaController: {
    /**
     * This factory is called whenever `fastify.register()` is called.
     * It may receive as input the schemas of the parent context if some schemas have been added.
     * @param {object} parentSchemas these schemas will be returned by the
     * `getSchemas()` method function of the returned `bucket`.
     */
    bucket: function factory (parentSchemas) {
      return {
        add (inputSchema) {
          // This function must store the schema added by the user.
          // This function is invoked when `fastify.addSchema()` is called.
        },
        getSchema (schema$id) {
          // This function must return the raw schema requested by the `schema$id`.
          // This function is invoked when `fastify.getSchema(id)` is called.
          return aSchema
        },
        getSchemas () {
          // This function must return all the schemas referenced by the routes schemas' $ref
          // It must return a JSON where the property is the schema `$id` and the value is the raw JSON Schema.
          const allTheSchemaStored = {
            'schema$id1': schema1,
            'schema$id2': schema2
          }
          return allTheSchemaStored
        }
      }
    },

    /**
     * The compilers factory lets you fully control the validator and serializer
     * in the Fastify's lifecycle, providing the encapsulation to your compilers.
     */
    compilersFactory: {
      /**
       * This factory is called whenever a new validator instance is needed.
       * It may be called whenever `fastify.register()` is called only if new schemas have been added to the
       * encapsulation context.
       * It may receive as input the schemas of the parent context if some schemas have been added.
       * @param {object} externalSchemas these schemas will be returned by the
       * `bucket.getSchemas()`. Needed to resolve the external references $ref.
       * @param {object} ajvServerOption the server `ajv` options to build your compilers accordingly
       */
      buildValidator: function factory (externalSchemas, ajvServerOption) {
        // This factory function must return a schema validator compiler.
        // See [#schema-validator](./Validation-and-Serialization.md#schema-validator) for details.
        const yourAjvInstance = new Ajv(ajvServerOption.customOptions)
        return function validatorCompiler ({ schema, method, url, httpPart }) {
          return yourAjvInstance.compile(schema)
        }
      },

      /**
       * This factory is called whenever a new serializer instance is needed.
       * It may be called whenever `fastify.register()` is called only if new schemas have been added to the
       * encapsulation context.
       * It may receive as input the schemas of the parent context if some schemas have been added.
       * @param {object} externalSchemas these schemas will be returned by the
       * `bucket.getSchemas()`. Needed to resolve the external references $ref.
       * @param {object} serializerOptsServerOption the server `serializerOpts`
       * options to build your compilers accordingly
       */
      buildSerializer: function factory (externalSchemas, serializerOptsServerOption) {
        // This factory function must return a schema serializer compiler.
        // See [#schema-serializer](./Validation-and-Serialization.md#schema-serializer) for details.
        return function serializerCompiler ({ schema, method, url, httpStatus, contentType }) {
          return data => JSON.stringify(data)
        }
      }
    }
  }
});

setNotFoundHandler

fastify.setNotFoundHandler(handler(request, reply)): set the 404 handler. This call is encapsulated by prefix, so different plugins can set different not found handlers if a different prefix option is passed to fastify.register(). The handler is treated as a regular route handler so requests will go through the full Fastify lifecycle. async-await is supported as well.

You can also register preValidation and preHandler hooks for the 404 handler.

Note The preValidation hook registered using this method will run for a route that Fastify does not recognize and not when a route handler manually calls reply.callNotFound. In which case, only preHandler will be run.

fastify.setNotFoundHandler({
  preValidation: (req, reply, done) => {
    // your code
    done()
  },
  preHandler: (req, reply, done) => {
    // your code
    done()
  }
}, function (request, reply) {
    // Default not found handler with preValidation and preHandler hooks
})

fastify.register(function (instance, options, done) {
  instance.setNotFoundHandler(function (request, reply) {
    // Handle not found request without preValidation and preHandler hooks
    // to URLs that begin with '/v1'
  })
  done()
}, { prefix: '/v1' })

Fastify calls setNotFoundHandler to add a default 404 handler at startup before plugins are registered. If you would like to augment the behavior of the default 404 handler, for example with plugins, you can call setNotFoundHandler with no arguments fastify.setNotFoundHandler() within the context of these registered plugins.

Note Some config properties from the request object will be undefined inside the custom not found handler. E.g.: request.routerPath, routerMethod and context.config. This method design goal is to allow calling the common not found route. To return a per-route customized 404 response, you can do it in the response itself.

setErrorHandler

fastify.setErrorHandler(handler(error, request, reply)): Set a function that will be called whenever an error happens. The handler is bound to the Fastify instance and is fully encapsulated, so different plugins can set different error handlers. async-await is supported as well.

If the error statusCode is less than 400, Fastify will automatically set it to 500 before calling the error handler.

setErrorHandler will not catch:

  • errors thrown in an onResponse hook because the response has already been sent to the client. Use the onSend hook instead.
  • not found (404) errors. Use setNotFoundHandler instead.
fastify.setErrorHandler(function (error, request, reply) {
  // Log error
  this.log.error(error)
  // Send error response
  reply.status(409).send({ ok: false })
})

Fastify is provided with a default function that is called if no error handler is set. It can be accessed using fastify.errorHandler and it logs the error with respect to its statusCode.

const statusCode = error.statusCode
if (statusCode >= 500) {
  log.error(error)
} else if (statusCode >= 400) {
  log.info(error)
} else {
  log.error(error)
}

setChildLoggerFactory

fastify.setChildLoggerFactory(factory(logger, bindings, opts, rawReq)): Set a function that will be called when creating a child logger instance for each request which allows for modifying or adding child logger bindings and logger options, or returning a custom child logger implementation.

Child logger bindings have a performance advantage over per-log bindings because they are pre-serialized by Pino when the child logger is created.

The first parameter is the parent logger instance, followed by the default bindings and logger options which should be passed to the child logger, and finally the raw request (not a Fastify request object). The function is bound with this being the Fastify instance.

For example:

const fastify = require('fastify')({
  childLoggerFactory: function (logger, bindings, opts, rawReq) {
    // Calculate additional bindings from the request if needed
    bindings.traceContext = rawReq.headers['x-cloud-trace-context']
    return logger.child(bindings, opts)
  }
})

The handler is bound to the Fastify instance and is fully encapsulated, so different plugins can set different logger factories.

setGenReqId

fastify.setGenReqId(function (rawReq)) Synchronous function for setting the request-id for additional Fastify instances. It will receive the raw incoming request as a parameter. The provided function should not throw an Error in any case.

Especially in distributed systems, you may want to override the default ID generation behavior to handle custom ways of generating different IDs in order to handle different use cases. Such as observability or webhooks plugins.

For example:

const fastify = require('fastify')({
  genReqId: (req) => {
    return 'base'
  }
})

fastify.register((instance, opts, done) => {
  instance.setGenReqId((req) => {
    // custom request ID for `/webhooks`
    return 'webhooks-id'
  })
  done()
}, { prefix: '/webhooks' })

fastify.register((instance, opts, done) => {
  instance.setGenReqId((req) => {
    // custom request ID for `/observability`
    return 'observability-id'
  })
  done()
}, { prefix: '/observability' })

The handler is bound to the Fastify instance and is fully encapsulated, so different plugins can set a different request ID.

addConstraintStrategy

Function to add a custom constraint strategy. To register a new type of constraint, you must add a new constraint strategy that knows how to match values to handlers, and that knows how to get the constraint value from a request.

Add a custom constraint strategy using the fastify.addConstraintStrategy method:

const customResponseTypeStrategy = {
  // strategy name for referencing in the route handler `constraints` options
  name: 'accept',
  // storage factory for storing routes in the find-my-way route tree
  storage: function () {
    let handlers = {}
    return {
      get: (type) => { return handlers[type] || null },
      set: (type, store) => { handlers[type] = store }
    }
  },
  // function to get the value of the constraint from each incoming request
  deriveConstraint: (req, ctx) => {
    return req.headers['accept']
  },
  // optional flag marking if handlers without constraints can match requests that have a value for this constraint
  mustMatchWhenDerived: true
}

const router = Fastify();
router.addConstraintStrategy(customResponseTypeStrategy);

hasConstraintStrategy

The fastify.hasConstraintStrategy(strategyName) checks if there already exists a custom constraint strategy with the same name.

printRoutes

fastify.printRoutes(): Fastify router builds a tree of routes for each HTTP method. If you call the prettyPrint without specifying an HTTP method, it will merge all the trees into one and print it. The merged tree doesn't represent the internal router structure. Do not use it for debugging.

Remember to call it inside or after a ready call.

fastify.get('/test', () => {})
fastify.get('/test/hello', () => {})
fastify.get('/testing', () => {})
fastify.get('/testing/:param', () => {})
fastify.put('/update', () => {})

fastify.ready(() => {
  console.log(fastify.printRoutes())
  // └── /
  //     ├── test (GET)
  //     │   ├── /hello (GET)
  //     │   └── ing (GET)
  //     │       └── /
  //     │           └── :param (GET)
  //     └── update (PUT)
})

If you want to print the internal router tree, you should specify the method param. Printed tree will represent the internal router structure. You can use it for debugging.

  console.log(fastify.printRoutes({ method: 'GET' }))
  // └── /
  //     └── test (GET)
  //         ├── /hello (GET)
  //         └── ing (GET)
  //             └── /
  //                 └── :param (GET)

  console.log(fastify.printRoutes({ method: 'PUT' }))
  // └── /
  //     └── update (PUT)

fastify.printRoutes({ commonPrefix: false }) will print compressed trees. This may be useful when you have a large number of routes with common prefixes. It doesn't represent the internal router structure. Do not use it for debugging.

  console.log(fastify.printRoutes({ commonPrefix: false }))
  // ├── /test (GET)
  // │   ├── /hello (GET)
  // │   └── ing (GET)
  // │       └── /:param (GET)
  // └── /update (PUT)

fastify.printRoutes({ includeMeta: (true | []) }) will display properties from the route.store object for each displayed route. This can be an array of keys (e.g. ['onRequest', Symbol('key')]), or true to display all properties. A shorthand option, fastify.printRoutes({ includeHooks: true }) will include all hooks.

  fastify.get('/test', () => {})
  fastify.get('/test/hello', () => {})

  const onTimeout = () => {}

  fastify.addHook('onRequest', () => {})
  fastify.addHook('onTimeout', onTimeout)

  console.log(fastify.printRoutes({ includeHooks: true, includeMeta: ['errorHandler'] }))
  // └── /
  //     └── test (GET)
  //         • (onTimeout) ["onTimeout()"]
  //         • (onRequest) ["anonymous()"]
  //         • (errorHandler) "defaultErrorHandler()"
  //         test (HEAD)
  //         • (onTimeout) ["onTimeout()"]
  //         • (onRequest) ["anonymous()"]
  //         • (onSend) ["headRouteOnSendHandler()"]
  //         • (errorHandler) "defaultErrorHandler()"
  //         └── /hello (GET)
  //             • (onTimeout) ["onTimeout()"]
  //             • (onRequest) ["anonymous()"]
  //             • (errorHandler) "defaultErrorHandler()"
  //             /hello (HEAD)
  //             • (onTimeout) ["onTimeout()"]
  //             • (onRequest) ["anonymous()"]
  //             • (onSend) ["headRouteOnSendHandler()"]
  //             • (errorHandler) "defaultErrorHandler()"

  console.log(fastify.printRoutes({ includeHooks: true }))
  // └── /
  //     └── test (GET)
  //         • (onTimeout) ["onTimeout()"]
  //         • (onRequest) ["anonymous()"]
  //         test (HEAD)
  //         • (onTimeout) ["onTimeout()"]
  //         • (onRequest) ["anonymous()"]
  //         • (onSend) ["headRouteOnSendHandler()"]
  //         └── /hello (GET)
  //             • (onTimeout) ["onTimeout()"]
  //             • (onRequest) ["anonymous()"]
  //             /hello (HEAD)
  //             • (onTimeout) ["onTimeout()"]
  //             • (onRequest) ["anonymous()"]
  //             • (onSend) ["headRouteOnSendHandler()"]

printPlugins

fastify.printPlugins(): Prints the representation of the internal plugin tree used by the avvio, useful for debugging require order issues.

Remember to call it inside or after a ready call.

fastify.register(async function foo (instance) {
  instance.register(async function bar () {})
})
fastify.register(async function baz () {})

fastify.ready(() => {
  console.error(fastify.printPlugins())
  // will output the following to stderr:
  // └── root
  //     ├── foo
  //     │   └── bar
  //     └── baz
})

addContentTypeParser

fastify.addContentTypeParser(content-type, options, parser) is used to pass a custom parser for a given content type. Useful for adding parsers for custom content types, e.g. text/json, application/vnd.oasis.opendocument.text. content-type can be a string, string array or RegExp.

// The two arguments passed to getDefaultJsonParser are for ProtoType poisoning
// and Constructor Poisoning configuration respectively. The possible values are
// 'ignore', 'remove', 'error'. ignore  skips all validations and it is similar
// to calling JSON.parse() directly. See the
// [`secure-json-parse` documentation](https://github.com/fastify/secure-json-parse#api) for more information.

fastify.addContentTypeParser('text/json', { asString: true }, fastify.getDefaultJsonParser('ignore', 'ignore'))

hasContentTypeParser

fastify.hasContentTypeParser(contentType) is used to check whether there is a content type parser in the current context for the specified content type.

fastify.hasContentTypeParser('text/json')

fastify.hasContentTypeParser(/^.+\/json$/)

removeContentTypeParser

fastify.removeContentTypeParser(contentType) is used to remove content type parsers in the current context. This method allows for example to remove the both built-in parsers for application/json and text/plain.

fastify.removeContentTypeParser('application/json')

fastify.removeContentTypeParser(['application/json', 'text/plain'])

removeAllContentTypeParsers

The fastify.removeAllContentTypeParsers() method allows all content type parsers in the current context to be removed. A use case of this method is the implementation of catch-all content type parser. Before adding this parser with fastify.addContentTypeParser() one could call the removeAllContentTypeParsers method.

For more details about the usage of the different content type parser APIs see here.

getDefaultJsonParser

fastify.getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning) takes two arguments. First argument is ProtoType poisoning configuration and second argument is constructor poisoning configuration. See the secure-json-parse documentation for more information.

defaultTextParser

fastify.defaultTextParser() can be used to parse content as plain text.

fastify.addContentTypeParser('text/json', { asString: true }, fastify.defaultTextParser)

errorHandler

fastify.errorHandler can be used to handle errors using fastify's default error handler.

fastify.get('/', {
  errorHandler: (error, request, reply) => {
    if (error.code === 'SOMETHING_SPECIFIC') {
      reply.send({ custom: 'response' })
      return
    }

    fastify.errorHandler(error, request, response)
  }
}, handler)

childLoggerFactory

fastify.childLoggerFactory returns the custom logger factory function for the Fastify instance. See the childLoggerFactory config option for more info.

Symbol.asyncDispose

fastify[Symbol.asyncDispose] is a symbol that can be used to define an asynchronous function that will be called when the Fastify instance is closed.

It's commonly used alongside the using TypeScript keyword to ensure that resources are cleaned up when the Fastify instance is closed.

This combines perfectly inside short lived processes or unit tests, where you must close all Fastify resources after returning from inside the function.

test('Uses app and closes it afterwards', async () => {
  await using app = fastify();
  // do something with app.
})

In the above example, Fastify is closed automatically after the test finishes.

Read more about the ECMAScript Explicit Resource Management and the using keyword introduced in TypeScript 5.2.

initialConfig

fastify.initialConfig: Exposes a frozen read-only object registering the initial options passed down by the user to the Fastify instance.

The properties that can currently be exposed are:

  • connectionTimeout
  • keepAliveTimeout
  • bodyLimit
  • caseSensitive
  • allowUnsafeRegex
  • http2
  • https (it will return false/true or { allowHTTP1: true/false } if explicitly passed)
  • ignoreTrailingSlash
  • disableRequestLogging
  • maxParamLength
  • onProtoPoisoning
  • onConstructorPoisoning
  • pluginTimeout
  • requestIdHeader
  • requestIdLogLabel
  • http2SessionTimeout
  • useSemicolonDelimiter
const { readFileSync } = require('node:fs')
const Fastify = require('fastify')

const fastify = Fastify({
  https: {
    allowHTTP1: true,
    key: readFileSync('./fastify.key'),
    cert: readFileSync('./fastify.cert')
  },
  logger: { level: 'trace'},
  ignoreTrailingSlash: true,
  maxParamLength: 200,
  caseSensitive: true,
  trustProxy: '127.0.0.1,192.168.1.1/24',
})

console.log(fastify.initialConfig)
/*
will log :
{
  caseSensitive: true,
  https: { allowHTTP1: true },
  ignoreTrailingSlash: true,
  maxParamLength: 200
}
*/

fastify.register(async (instance, opts) => {
  instance.get('/', async (request, reply) => {
    return instance.initialConfig
    /*
    will return :
    {
      caseSensitive: true,
      https: { allowHTTP1: true },
      ignoreTrailingSlash: true,
      maxParamLength: 200
    }
    */
  })

  instance.get('/error', async (request, reply) => {
    // will throw an error because initialConfig is read-only
    // and can not be modified
    instance.initialConfig.https.allowHTTP1 = false

    return instance.initialConfig
  })
})

// Start listening.
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})