Plugins
Plugins
Fastify allows the user to extend its functionalities with plugins. A plugin can
be a set of routes, a server decorator, or whatever. The API
that you will need to use one or more plugins, is register
.
By default, register
creates a new scope, this means that if you make some
changes to the Fastify instance (via decorate
), this change will not be
reflected by the current context ancestors, but only by its descendants. This
feature allows us to achieve plugin encapsulation and inheritance, in this
way we create a directed acyclic graph (DAG) and we will not have issues
caused by cross dependencies.
You may have already seen in the Getting Started guide how easy it is to use this API:
fastify.register(plugin, [options])
Plugin Options
The optional options
parameter for fastify.register
supports a predefined
set of options that Fastify itself will use, except when the plugin has been
wrapped with fastify-plugin. This
options object will also be passed to the plugin upon invocation, regardless of
whether or not the plugin has been wrapped. The currently supported list of
Fastify specific options is:
Note: Those options will be ignored when used with fastify-plugin
It is possible that Fastify will directly support other options in the future.
Thus, to avoid collisions, a plugin should consider namespacing its options. For
example, a plugin foo
might be registered like so:
fastify.register(require('fastify-foo'), {
prefix: '/foo',
foo: {
fooOption1: 'value',
fooOption2: 'value'
}
})
If collisions are not a concern, the plugin may simply accept the options object as-is:
fastify.register(require('fastify-foo'), {
prefix: '/foo',
fooOption1: 'value',
fooOption2: 'value'
})
The options
parameter can also be a Function
that will be evaluated at the
time the plugin is registered while giving access to the Fastify instance via
the first positional argument:
const fp = require('fastify-plugin')
fastify.register(fp((fastify, opts, done) => {
fastify.decorate('foo_bar', { hello: 'world' })
done()
}))
// The opts argument of fastify-foo will be { hello: 'world' }
fastify.register(require('fastify-foo'), parent => parent.foo_bar)
The Fastify instance passed on to the function is the latest state of the
external Fastify instance the plugin was declared on, allowing access to
variables injected via decorate
by preceding plugins
according to the order of registration. This is useful in case a plugin
depends on changes made to the Fastify instance by a preceding plugin i.e.
utilizing an existing database connection to wrap around it.
Keep in mind that the Fastify instance passed on to the function is the same as
the one that will be passed into the plugin, a copy of the external Fastify
instance rather than a reference. Any usage of the instance will behave the same
as it would if called within the plugins function i.e. if decorate
is called,
the decorated variables will be available within the plugins function unless it
was wrapped with fastify-plugin
.
Route Prefixing option
If you pass an option with the key prefix
with a string
value, Fastify will
use it to prefix all the routes inside the register, for more info check
here.
Be aware that if you wrap your routes with
fastify-plugin
, this option will
not work (there is a workaround available).
Error handling
The error handling is done by avvio.
As a general rule, it is highly recommended that you handle your errors in the
next after
or ready
block, otherwise you will get them inside the listen
callback.
fastify.register(require('my-plugin'))
// `after` will be executed once
// the previous declared `register` has finished
fastify.after(err => console.log(err))
// `ready` will be executed once all the registers declared
// have finished their execution
fastify.ready(err => console.log(err))
// `listen` is a special ready,
// so it behaves in the same way
fastify.listen({ port: 3000 }, (err, address) => {
if (err) console.log(err)
})
async/await
async/await is supported by after
, ready
, and listen
, as well as
fastify
being a Thenable.
await fastify.register(require('my-plugin'))
await fastify.after()
await fastify.ready()
await fastify.listen({ port: 3000 })
Note: Using await
when registering a plugin loads the plugin
and the underlying dependency tree, "finalizing" the encapsulation process.
Any mutations to the plugin after it and its dependencies have been
loaded will not be reflected in the parent instance.
ESM support
ESM is supported as well from Node.js
v13.3.0
and above!
// main.mjs
import Fastify from 'fastify'
const fastify = Fastify()
fastify.register(import('./plugin.mjs'))
fastify.listen({ port: 3000 }, console.log)
// plugin.mjs
async function plugin (fastify, opts) {
fastify.get('/', async (req, reply) => {
return { hello: 'world' }
})
}
export default plugin
Create a plugin
Creating a plugin is very easy, you just need to create a function that takes
three parameters, the fastify
instance, an options
object, and the done
callback.
Example:
module.exports = function (fastify, opts, done) {
fastify.decorate('utility', function () {})
fastify.get('/', handler)
done()
}
You can also use register
inside another register
:
module.exports = function (fastify, opts, done) {
fastify.decorate('utility', function () {})
fastify.get('/', handler)
fastify.register(require('./other-plugin'))
done()
}
Sometimes, you will need to know when the server is about to close, for example,
because you must close a connection to a database. To know when this is going to
happen, you can use the 'onClose'
hook.
Do not forget that register
will always create a new Fastify scope, if you do
not need that, read the following section.
Handle the scope
If you are using register
only for extending the functionality of the server
with decorate
, it is your responsibility to tell Fastify
not to create a new scope. Otherwise, your changes will not be accessible by the
user in the upper scope.
You have two ways to tell Fastify to avoid the creation of a new context:
- Use the
fastify-plugin
module - Use the
'skip-override'
hidden property
We recommend using the fastify-plugin
module, because it solves this problem
for you, and you can pass a version range of Fastify as a parameter that your
plugin will support.
const fp = require('fastify-plugin')
module.exports = fp(function (fastify, opts, done) {
fastify.decorate('utility', function () {})
done()
}, '0.x')
Check the fastify-plugin
documentation to learn more about how to use this module.
If you do not use the fastify-plugin
module, you can use the 'skip-override'
hidden property, but we do not recommend it. If in the future the Fastify API
changes it will be your responsibility to update the module, while if you use
fastify-plugin
, you can be sure about backward compatibility.
function yourPlugin (fastify, opts, done) {
fastify.decorate('utility', function () {})
done()
}
yourPlugin[Symbol.for('skip-override')] = true
module.exports = yourPlugin