fastify-explorer
v2.1.0
Published
Explore your fastify's instances
Downloads
4
Maintainers
Readme
fastify-explorer
Explore your fastify instances.
If you understand how to use this plugin, you will archive the higher knowledge in the fastify encapsulation mechanism ✨
Note
To deep dive the fastify encapsulation, you may want to try the fastify-overview plugin as well.
Install
npm install fastify-explorer
Compatibility
| Plugin version | Fastify version |
| ------------- |:---------------:|
| ^1.0.0
| ^2.0.0
|
| ^2.0.0
| ^4.0.0
|
Usage
The plugin will store a pointer to the fastify instances you are interested in, created with the .register
function that will have the explorer
configuration.
This operation will break the encapsulation and you must be confident with the plugin system.
If you want to understand better the Fastify plugin system, give a look to this blog post.
const Fastify = require('fastify')
const fastifyExplorer = require('fastify-explorer')
const routes = require('./my-routes')
const app = Fastify()
app.register(fastifyExplorer, { optionKey: 'explorer' })
app.register(routes, { explorer: { name: 'routes-explorer' } })
Now you can access the routes-explorer
fastify instance with the giveMe
function!
See the API section for more details.
Options
optionKey
: the key to use to register the plugin name. Default:explorer
Use case: testing
Let me show how I develop this plugin:
Usually, I need to read data from a DB, like mongodb, and if you want 100% coverage, you need to
test the error
case that usually is archived mocking the data or even the db driver.
So I prefer to get the mongodb connection of my fastify server.. and close it to let it throws! The encapsulation hides the registered plugins unless using this plugin 😈
This is only an example, but you can get whatever decorator is attached to all the fastify instances! Read the code-comments to have a complete overview of this plugin.
Moroever, this plugin forces you to develop your application with a solid pattern. Let's see it:
application.js
: factory pattern
This is the main file of your app, here you must create the fastify server and register plugins and routes.
Remember: don't call .listen
but simply return your server!
Note that this plugin must be the first one to be registered.
const Fastify = require('fastify')
const fastifyMongo = require('@fastify/mongodb')
const fastifyExplorer = require('fastify-explorer')
const routes = require('./my-routes')
module.exports = function build (config, explorer) {
const fastify = Fastify(config.server)
// use it only for test. if you don't nothing bad happens, only waste of RAM
if (explorer === true) {
// the register must be the FIRST ONE
fastify.register(fastifyExplorer)
}
fastify.register(fastifyMongo, { url: config.mongoUrl })
// To activate the plugin, you need to add and `explorer` config in registration phase
// If you don't register the plugin, the parameters are just ignored
fastify.register(routes, { explorer: { name: 'routes-explorer' } })
return fastify
}
server.js
: launcher
Now you can run your application with fastify-cli
or with the following code.
This file is a dumb file.. so we don't need to test it!
const buildApp = require('./application')
const conf = require('./config.json')
const server = buildApp(conf)
server.listen(3000, (err) => {
if (err) {
console.log(err)
process.exit(1)
}
})
Test
Finally, we can test our code! Note how our test is like the launcher.
const { test } = require('tap')
const buildApp = require('./application')
const conf = require('./config-test.json')
const server = buildApp(conf)
test('call my route and fail', t => {
t.plan(4)
const fastify = buildApp(conf, true) // we pass TRUE only in test!
fastify.ready(err => {
t.error(err)
// this mongo instance would be unreachable because hidden in the encapsulated context
const mongoInsideFastify = fastify.giveMe('routes-explorer', 'mongo')
// let's close the connection, so our routes will break!
mongoInsideFastify.client.close()
fastify.inject('/my-route', (err, res) => {
t.error(err)
t.equals(res.statusCode, 501) // this is a customized http status code
t.deepEquals(res.json(), {}) // empty mongo :)
})
})
})
API
This plugin adds two decorators to the fastify instance:
giveMe(instanceName: string[, decoratorName: string])
: return the fastify instance registered with given name in parameters{ explorer: { name: 'routes-explorer' } }
registerPlugin(pluginFunction: function, pluginOpts: object, explorerOpts: string|object)
: register a plugin adding an encapsulation layer. It is only a shortcut to create new fastify contexts.
You can find an example for .giveMe
in this document.
Why registerPlugin
?
This plugin works only when you register an encapsulated context. The fastify-plugin
system breaks the encapsulation.. so this plugin wouldn't work with normal plugins!
In other words, the onRegister
hook is called only when new contexts are created, and the plugins don't create new contexts!
Here to you and example:
app.register(fastifyExplorer)
.after(() => {
// use the decorator to register mongo
app.registerPlugin(fastifyMongo, { url: '...' }, 'theMongo')
})
I don't like this usage because it will impact too much on how you write your application.js
file.
Another important question
Somebody could ask: why pass the explorer
parameters in the route's registration and not in mongo in application.js
?
As explained in the registerPlugin
's section, it is useless writing because it doesn't work:
fastify.register(fastifyMongo, { url: config.mongoUrl }, { explorer: { name: 'mongo-code' } })
So, I have configured the explorer
parameters in the routes because that code will create a new context that would have access to the mongo instance!
It seems tricky, but I think it is right, because I have named the routes I want to break: I'm not interested to the plugins itself.
Conclusion
If you have read all this documentation, you are great and I hope you have understood better the encapsulation context and how to play with it.
Useful links:
- https://backend.cafe/the-complete-guide-to-the-fastify-plugin-system
- https://stackoverflow.com/questions/61020394/what-is-the-exact-use-of-fastify-plugin/61054534#61054534
- https://github.com/Eomm/fastify-overview#fastify-overview
Future
I'm thinking how to test complex scenarios like this one, where you want to break only the first findOne
call and not the second one.
fastify.get('/', async (req, res) => {
try {
const hello = await fastify.mongo.findOne({ _id: 'an-id' })
} catch(err) {
mqtt.publish('woooooo')
const world = await fastify.mongo.findOne({ _id: 'an-id-2' })
}
})
License
Copyright Manuel Spigolon, Licensed under MIT.