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.
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:
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:
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 :
There is only one root context, and all the plugins are children of it
The parent context can't access the state of the child contexts
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:
In a new app.js file, add the following code:
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.
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:
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.
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:
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
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:
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:
Now the application schema will look like this because we broke the ctx1 context, and the parent swallowed it:
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:
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:
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.
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
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 !