npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fastify-explorer

v2.1.0

Published

Explore your fastify's instances

Downloads

4

Readme

fastify-explorer

JavaScript Style Guide ci

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.