When you create a decorateApp
function, you are essentially designing your own way to load your application - like having your own custom "API".
However, one of the initial challenges you will encounter is deciding between handling it synchronously or asynchronously:
- The sync function is
decorateApp
- The async function is
decorateAppAsync
within another async function
For instance, you might need to fetch data from a database before initializing your application.
const decorateApp = (app) => {
app.register(require('@fastify/mongodb'))
};
const businessLogic = async (app) => {
const data = await app.mongo.db.collection('data').find({}).toArray()
}
decorateApp(app)
businessLogic(app) // Oops: this part is asynchronous
In such scenarios, several code changes are required:
- The
decorateApp
function must now be made async
- The mongodb registration should be awaited
- The core application loading code needs to be asynchronous as well
Alternatively, by following fastify's approach, only the database-loading plugin needs adjustment:
const applicationConfigPlugin = fp(
+ async function (fastify) {
- function (fastify, opts, next) {
- app.register(require('@fastify/mongodb'))
- next()
+ await app.register(require('@fastify/mongodb'))
}
)
Note that the example in fastify-plugin code does not include next
callback since it's a synchronous function.
An additional issue to lookout for is high hidden coupling among functions.
Each application requires a configuration. Normally, the fastify instance is enriched with it.
Hence, you may find:
decorateAppWithConfig(app);
decorateAppWithSomethingElse(app);
In this case, decorateAppWithSomethingElse
must realize that it is loaded after decorateAppWithConfig
.
With fastify-plugin
on board, you can simplify this process:
const applicationConfigPlugin = fp(
async function (fastify) {
fastify.decorate('config', 42);
},
{
name: 'my-app-config',
}
)
const applicationBusinessLogic = fp(
async function (fastify) {
// ...
},
{
name: 'my-app-business-logic',
dependencies: ['my-app-config']
}
)
// Incorrect order of applying plugins would now throw a clear error message:
app.register(applicationBusinessLogic);
app.register(applicationConfigPlugin);
This setup ensures better error handling and management of dependencies compared to manually structuring each function to work with fastify instances. The built-in validation checks provided by fastify-plugin
streamline these processes effectively.
While the example in question may seem straightforward, scaling up to larger applications can introduce complexity due to various factors like synchronicity, error messaging, and dependency management intricacies.
- Varying loading mechanisms (sync/async)
- Inadequate error feedback
- Implicit dependencies causing confusion