Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/Plugins-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,28 @@ module.exports = fp(dbPlugin)
```
You can also tell to `fastify-plugin` to check the installed version of Fastify, in case of you need a specific api.

As we mentioned earlier, Fastify starts loading its plugins __after__ `.listen()`, `.inject()` or `.ready()` are called and as such, __after__ they have been declared. This means that, even though the plugin may inject variables to the external fastify instance via [`decorate`](https://github.com/fastify/fastify/blob/master/docs/Decorators.md), the decorated variables will not be accessible before calling `.listen()`, `.inject()` or `.ready()`.

In case you rely on a variable injected by a preceding plugin and want to pass that in the `options` argument of `register`, you can do so by using a function instead of an object:
```js
const fastify = require('fastify')()
const fp = require('fastify-plugin')
const dbClient = require('db-client')

function dbPlugin (fastify, opts, next) {
dbClient.connect(opts.url, (err, conn) => {
fastify.decorate('db', conn)
next()
})
}

fastify.register(fp(dbPlugin), { url: 'https://example.com' })
fastify.register(require('your-plugin'), parent => {
return { connection: parent.db, otherOption: 'foo-bar' }
})
```
In the above example, the `parent` variable of the function passed in as the second argument of `register` is a copy of the **external fastify instance** that the plugin was registered at. This means that we are able to access any variables that were injected by preceding plugins in the order of declaration.

<a name="handle-errors"></a>
## Handle errors
It can happen that one of your plugins could fail during the startup. Maybe you expect it and you have a custom logic that will be triggered in that case. How can you do this?
Expand Down
19 changes: 19 additions & 0 deletions docs/Plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,25 @@ fastify.register(require('fastify-foo'), {
})
```

The `options` parameter can also be a `Function` which will be evaluated at the time the plugin is registered while giving access to the fastify instance via the first positional argument:

```js
const fp = require('fastify-plugin')

fastify.register(fp((fastify, opts, next) => {
fastify.decorate('foo_bar', { hello: 'world' })

next()
}))

// 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`](https://github.com/fastify/fastify/blob/master/docs/Decorators.md) 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 f.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 in to 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 plugin's function i.e. if `decorate` is called, the decorated variables will be available within the plugin's function unless it was wrapped with [`fastify-plugin`](https://github.com/fastify/fastify-plugin).

<a name="route-prefixing-option"></a>
#### 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](https://github.com/fastify/fastify/blob/master/docs/Routes.md#route-prefixing).<br>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
"dependencies": {
"abstract-logging": "^1.0.0",
"ajv": "^6.9.2",
"avvio": "^6.1.0",
"avvio": "^6.1.1",
"bourne": "^1.1.2",
"fast-json-stringify": "^1.11.2",
"find-my-way": "^2.0.0",
Expand Down
79 changes: 79 additions & 0 deletions test/plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,85 @@ test('fastify.register with fastify-plugin should not incapsulate his code', t =
})
})

test('fastify.register with fastify-plugin should provide access to external fastify instance if opts argument is a function', t => {
t.plan(22)
const fastify = Fastify()

fastify.register((instance, opts, next) => {
instance.register(fp((i, o, n) => {
Comment thread
alex-ppg marked this conversation as resolved.
i.decorate('global', () => {})
t.ok(i.global)
n()
}))

instance.register((i, o, n) => n(), p => {
t.notOk(p === instance || p === fastify)
t.ok(instance.isPrototypeOf(p))
t.ok(fastify.isPrototypeOf(p))
t.ok(p.global)
})

instance.register((i, o, n) => {
i.decorate('local', () => {})
n()
})

instance.register((i, o, n) => n(), p => t.notOk(p.local))

instance.register((i, o, n) => {
t.ok(i.local)
n()
}, p => p.decorate('local', () => {}))

instance.register((i, o, n) => n(), p => t.notOk(p.local))

instance.register(fp((i, o, n) => {
t.ok(i.global_2)
n()
}), p => p.decorate('global_2', () => 'hello'))

instance.register((i, o, n) => {
i.decorate('global_2', () => 'world')
n()
}, p => p.get('/', (req, reply) => {
t.ok(p.global_2)
reply.send({ hello: p.global_2() })
}))

t.notOk(instance.global)
t.notOk(instance.global_2)
t.notOk(instance.local)

// the decoration is added at the end
instance.after(() => {
t.ok(instance.global)
t.strictEqual(instance.global_2(), 'hello')
t.notOk(instance.local)
})

next()
})

fastify.ready(() => {
t.notOk(fastify.global)
})

fastify.listen(0, err => {
t.error(err)
fastify.server.unref()

sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.strictEqual(response.statusCode, 200)
t.strictEqual(response.headers['content-length'], '' + body.length)
t.deepEqual(JSON.parse(body), { hello: 'world' })
})
})
})

test('fastify.register with fastify-plugin registers root level plugins', t => {
t.plan(15)
const fastify = Fastify()
Expand Down