Plugins & encapsulation
Plugins package routes, hooks, decorators, and lifecycle callbacks into reusable units. Like Fastify, DaloyJS gives each plugin a local scope: app-level hooks and decorators flow inward, while plugin-level hooks and decorators do not leak sideways to sibling plugins.
Defining a plugin
A plugin can be a descriptor object with optional metadata and a register(app) function, or a plain function that receives the child app. Name plugins that manage shared state so DaloyJS can deduplicate them and validate dependencies.
Registering a plugin
app.register() mounts the plugin in a scoped group. The registration config supplies the inherited prefix, tags, hooks, and route auth metadata for every route the plugin adds.
Await app.ready() before starting the server whenever a plugin does async work. Sync plugins also use the same queue for async install observers, so it is safe to call every time.
Dependencies, seeds, and state
Plugin descriptors can declare operational metadata that DaloyJS checks at registration time:
dependencies: prerequisite plugin names that must already be registered.seed: a differentiator for mounting the same named plugin more than once with different configuration.stateful: production guard for plugins that mutate shared state. Anonymous stateful plugins are refused unless you give them a name.
Decorators
Decorate the app to inject shared resources into every handler's state. Decorations added at the root are visible inside plugins. Decorations added inside a plugin stay scoped to that plugin. Reusing a key throws unless you pass { override: true }deliberately.
Extension ordering
Plugins that contribute lifecycle hooks can declare ordered extensions. DaloyJS topologically sorts before and after relationships, rejects duplicate extension names, and refuses two extensions that mutate the same response header without an explicit order.
Why encapsulation matters
- You can mount the same plugin twice under different prefixes without hook bleed-through.
- Third-party plugins cannot accidentally rewrite sibling hooks or error handlers.
- Prefixes, tags, hooks, auth, and decorators stay predictable as a service grows.
- Named stateful plugins are deduplicated, and dependencies fail fast when the registration order is wrong.
Lifecycle events
Observability plugins often need to know when other plugins finish installing or when the process starts shutting down. DaloyJS exposes two event hooks for this without polluting the route registry:
app.onPluginInstalled(listener): fires once perregister()call, after sync plugins return and after async plugins resolve. The listener receives{ name?: string, prefix: string }, whereprefixis the effective mounted prefix after nesting. Awaitingapp.ready()drains async plugins and async listeners.app.onShutdown(listener): fires at the start ofapp.shutdown(timeoutMs, reason), before in-flight requests drain. Use this to flush metrics, publish a draining signal to a load balancer, or close background pollers. For post-drain cleanup such as database pools and file handles, keep usingonClose().
Listener errors are caught and logged via the configured logger so a faulty observer does not crash plugin registration or graceful shutdown. Both shutdown() and the underlying onClose chain remain idempotent.