Avvio: Asynchronous Application Bootstrapping
Avvio is a robust, reentrant, and graph-based library designed for managing the asynchronous bootstrapping of Node.js applications. It simplifies complex application startup sequences by handling plugin loading order, inter-plugin dependencies, and comprehensive error management automatically. Unlike simpler sequential loaders, Avvio allows plugins to register other plugins, ensuring correct execution flow within deeply nested structures. The current stable version is 9.2.0, with a release cadence of minor and patch updates every few weeks or months, often driven by dependency updates or minor feature enhancements. Its key differentiator is its emphasis on reentrancy and a dependency graph, which guarantees that even deeply nested plugins are initialized in the precise order required, coupled with sophisticated error handling capabilities.
Common errors
-
AVV_ERR_READY_TIMEOUT: Plugin timed out after Xms
cause A plugin's initialization function (either its callback or its returned Promise) did not resolve within the `timeout` duration configured during Avvio instantiation.fixIncrease the `timeout` option when initializing Avvio (e.g., `avvio({}, { timeout: 15000 })`) or, preferably, debug the plugin to identify and resolve performance bottlenecks that are causing the delay. -
TypeError: app.register is not a function
cause Attempting to use `app.register` to add plugins, a method commonly found in the Fastify framework, instead of Avvio's `app.use` method.fixReplace `app.register(myPlugin, options)` with `app.use(myPlugin, options)` as Avvio's plugin registration method is named `use`. -
TypeError: (0 , avvio_1.default) is not a function
cause This error typically occurs when using TypeScript or transpiled JavaScript, indicating an incorrect default import from a CommonJS module or an attempt to call the `default` export incorrectly.fixEnsure correct ESM import syntax: `import avvio from 'avvio'; const app = avvio();`. If using CommonJS, ensure `const avvio = require('avvio'); const app = avvio();`. -
Error: AVV_ERR_HOOK_TIMEOUT: Hook 'after' timed out after Xms
cause An `after` hook, which runs after a set of plugins, failed to complete its execution (call its callback or resolve its Promise) within the configured timeout duration.fixExamine the `after` hook function to identify any long-running or blocking operations. Either optimize the hook's performance, or increase the global `timeout` option if the delay is expected and acceptable.
Warnings
- breaking Avvio v9.0.0 introduced breaking changes by dropping support for several End-of-Life Node.js versions. Applications running on older or unsupported Node.js runtimes will need to upgrade their Node.js environment to use Avvio v9.x or later.
- gotcha Plugins can time out if they take longer than the configured `timeout` option to complete their initialization, leading to an `AVV_ERR_READY_TIMEOUT` error. While the default `timeout` is `0` (disabled), setting it to a non-zero value without careful consideration can cause unexpected startup failures if plugins exceed the configured duration.
- gotcha While Avvio supports both callback-style and Promise-returning (async/await) plugins, mixing them in a way where an `async` function still calls `cb()` can lead to unexpected behavior, double-done situations, or hung applications. It's crucial to be consistent.
- breaking Prior to v9.1.0, even synchronous plugins were expected to call a callback to signal completion. Avvio v9.1.0 introduced support for synchronous plugins that *do not* call a callback, meaning older versions would hang indefinitely if a synchronous plugin omitted the callback.
Install
-
npm install avvio -
yarn add avvio -
pnpm add avvio
Imports
- avvio
const avvio = require('avvio')import avvio from 'avvio'
- Avvio
import { Avvio } from 'avvio'; const app: Avvio<MyApplicationInstance> = avvio(myAppInstance); - use
app.register(myPlugin, options)
app.use(myPlugin, options)
Quickstart
'use strict'
const avvio = require('avvio')
const app = avvio()
app
.use(first, { hello: 'world' })
.after((err, cb) => {
if (err) {
console.error('Error in after hook:', err);
return cb(err);
}
console.log('after first and second')
cb()
})
app.use(third)
app.ready(function (err) {
// the error must be handled somehow
if (err) {
throw err
}
console.log('application booted!')
// In a real app, you might start listening for requests here
// For demonstration, we'll close the app immediately
app.close().then(() => console.log('application closed.')).catch(e => console.error('Error closing:', e));
})
function first (instance, opts, cb) {
console.log('first loaded', opts)
instance.use(second)
cb()
}
function second (instance, opts, cb) {
console.log('second loaded')
process.nextTick(cb)
}
// async/await or Promise support
async function third (instance, opts) {
console.log('third loaded')
// Simulating async work
await new Promise(resolve => setTimeout(resolve, 50));
}