Editor’s note: This is a cross-post written by Senior Software Developer, Manuel Spigolon. Manuel has his own blog at backend.cafe where you can subscribe for updates and find more great posts. Some of the links in the article point to Manuel’s personal github account.

Understanding the Fastify Plugin System

One of the most important differentiating features of Fastify is its Plugin System and the encapsulation concept. These two features are synonymous as we are talking about the same thing.

The Fastify Plugin System is a powerful tool that allows you to build a modular application. Unfortunately, it is also the most misunderstood feature of Fastify. This article will show how it works and how to avoid common mistakes. We are going to view the encapsulation with some images to make it easier to understand.

What is encapsulation?

Encapsulation is not a new engineering concept nor a Fastify exclusive feature. It is the base concept of the Fastify Plugin System, and you can’t ignore it if you want to take advantage of Fastify.

You can read its formal description on the Wikipedia page.

Briefly, it is a mechanism that allows you to create an isolated context from another application’s contexts.

Here is a simple representation of three encapsulated contexts:

3 Boxes labeled: context 1, context 2, And context n.

What does it mean? This concept may seem trivial, but it is very powerful:

  • Each context can have its own state.
  • Each context can’t access the state of other contexts.
  • It is possible to create a context with the same code but different states.

Encapsulation and Inheritance

The encapsulation concept can be pushed to the extreme by adding the inheritance behaviour. In this case, the encapsulation is not only a mechanism to isolate the contexts, but also a mechanism to create a new context from others.

Here is a descriptive image:

3 Boxes in top row labeled: context 1, context 2, And context n. An arrow points down from context 1 to a box called context X. An arrow points down and to the left from context 2 to a box labelled Context Y which has an arrow pointing down to a box labelled Context K. An arrow points down and to the right of context 2 to a box labelled context z.

This new image shows us three different Rooted trees. It is more complex, but it is very powerful because it defines new behaviours:

  • Each context may have child contexts.
  • Each context could have at most one parent context.
  • Each context can access the state of its parent context.

That being said, we have mentioned all the concepts we need to understand the Fastify encapsulation.

Let’s see how it works in Fastify.

The Fastify Plugin System demystified

In one sentence: Fastify’s Plugin System is an encapsulated context builder. No more, no less.

The Plugin System has these pillars:

  1. There is only one root context, and all the plugins are children of it
  2. The parent context can’t access the state of the child contexts
  3. The children’s context can access the state of the parent’s context

I’m used to calling the root context the root application instance and every child context as plugin instance.

Since we have a root context, every Fastify application can be represented as a tree structure where every node is a context.

Let’s take a look at some code to better understand how it works.

Create a new project:

Copy to Clipboard

In a new app.js file, add the following code:

Copy to Clipboard

The app variable in the previous code is the root application instance or Fastify’s root context.

Let’s try to create some contexts, as shown in the previous image.

To do this, we need to call the register method.

Edit the start function as follows:

Copy to Clipboard

By doing this, we are creating two child contexts from the root context. Note that the first argument of the register method is a function that represents the plugin instance. Its first argument, named instance is the context itself and gives us access to all of Fastify’s server methods.

We can visualize our code structure as follows, but it will look like the previous image that we recreate with the Fastify Plugin System:

A box labeled Root Application Instance with an two arrows, one to the left and one to the right. The left arrow points to a box labelled Context 1 which has an arrow pointing down to a box labelled Context X. The right arrow points to a box labelled Context 2 which has an arrow pointing down to the left to a box labelled Context Y which has an arrow pointing down to a box labelled Context K. Context also has an arrow pointing down and to the right to a box labelled Context Z.

As you can see, the root context is the parent of the two children contexts. This representation shows the tree structure of the application.

But, what is the difference between each context?

The encapsulated context state

As we said before, each context has its own state. In Fastify, the state is represented by:

  • The context’s routes
  • The context’s settings and plugins
  • The context’s decorators
  • The context’s hooks

This means that every time you create a new context by calling the register method, you can pass a new set of options, settings, etc. Moreover, the inheritance implementation inherits the parent context’s state.

In practice, this means that the child contexts inherit the parent’s hooks, decorators, settings and plugins.

Copy to Clipboard

This behaviour is the brain of the Fastify Plugin System. In fact, you can play with the encapsulation to create a plugin that can be used in different contexts.

For example, we could modify to get the same result but reuse the same plugin function:

Copy to Clipboard

Here it should become clearer how encapsulation works and its benefits:

  • you can split your source code into different source files/plugins
  • load plugins with different settings (e.g.: connect to many MongoDB connections by registering the plugin twice and changing the connection URL)
  • add hooks to a limited set of routes: the ones defined in the encapsulation context

In fact, if you try to rewrite our examples by moving the plugin function to a file, you will verify that you can’t access the JavaScript closure anymore to read some external config, but you need to rely on the Fastify instance argument only!

This is Fastify’s secret sauce!

Learn More about our commitment to Open Source

Breaking the encapsulation

You may have seen the fastify-plugin module that “breaks the encapsulation”. But why do we need to break the encapsulation?

Let’s see the previous example’s structure:

A box labeled Root Application Instance with two arrows, one to the left and one to the right. The left arrow points to a box labelled Context 1. The right arrow points to a box labelled Context 2.

If I would like to access the ctx1 decorator from the root context, I can’t do it, unless I break the encapsulation by wrapping the plugin function with the fastify-plugin module:

Copy to Clipboard

Now the application schema will look like this because we broke the ctx1 context, and the parent swallowed it:

A box labeled Root Application Instance with two arrows, one to the left and one to the right. The left arrow points to a box labelled Context 1. The right arrow points to a box labelled Context 2.

Now, the app instance has access to the ctx1 decorator.

Note that every plugin you will install will be wrapped with the fastify-plugin module, otherwise if the plugin adds a database connection, it will be encapsulated and inaccessible from the parent context!

Why is the Fastify Plugin System not easy to understand?

We have seen the Fastify Plugin System and how it works, but why is it not easy to understand?

The answer is simple: it is a powerful tool, but it is not easy to control when things become bigger.

The most common mistake is when you create a plugin hell like the following:

Copy to Clipboard

The plugin hell is not wrong, but it makes the code hard to read and understand. This denotes it is less maintainable, and you may be forced to add some global variables to solve some issues.

To solve this problem, you can use the fastify-overview plugin to visualize the tree structure and be able to navigate within your code base without any difficulties.

First, we need to install the plugin and configure it:

Copy to Clipboard

The code above starts the Fastify application and exports the tree structure in a JSON file. Now, you should have a file named appStructure.json in your project’s root directory.

The stored file can be used to visualize the tree structure of your application. You can use the fastify-overview-ui plugin or the https://jsoncrack.com website.

Copy the content of the appStructure.json file and paste it into the https://jsoncrack.com website, you will see the detailed data structure of your application, and you will be able to understand where each component is stored in your codebase.

Thanks to the plugin, you will be able to see the runtime application graph:

🛣 ALL the Fastify routes
🍱 ALL the Fastify plugins
🎨 ALL the Fastify decorators
🪝 ALL the Fastify hooks

For example, you will see any additional settings that a third-party plugin adds to your Fastify instance that you may not be aware of.

More from Manuel:
Upgrading Fastify’s input validation to Ajv version 8

Summary

You have read how Fastify implements encapsulation through the Plugin System and its powerful features. Moreover, you have seen how to use the fastify-overview plugin to visualize your application’s tree structure to debug and explore it more easily.

If you want to learn more about the Fastify Plugin System, you can pre-order the Fastify Book which will deep dive into this topic and a lot more!

If you enjoyed this article please share and follow me on Twitter @ManuEomm!

Do you need help with Digital Product Development? Contact us today to schedule a Discovery Workshop.

Share Me

Related Reading

Newsletter

Don’t miss a beat

Get all the latest NearForm news, from technology to design. Sign up for our newsletter.

Follow us for more information on this and other topics.