Skip to main content

Plugins

Extend your Navios application with plugins that hook into the application lifecycle.

Overview

Plugins allow you to extend Navios applications by executing code at specific points in the application lifecycle. Common use cases include:

  • Adding OpenAPI documentation
  • Integrating observability tools (OpenTelemetry, etc.)
  • Registering global middleware
  • Extending the dependency injection container
  • Adding custom routes or services

Plugin Lifecycle Stages

Navios executes plugins at specific stages during application initialization. Each stage provides access to different parts of the application context:

NaviosFactory.create() returns

app.usePlugin(...) ← register plugins here

app.init() starts

┌─ pre:modules-traverse → container only
├─ loadModules()
└─ post:modules-traverse → container + modules + moduleLoader

┌─ pre:adapter-resolve → container + modules + moduleLoader
├─ container.get(AdapterToken)
└─ post:adapter-resolve → + adapter

┌─ pre:adapter-setup → full context
├─ adapter.setupAdapter()
└─ post:adapter-setup → full context

┌─ pre:modules-init → full context
├─ adapter.onModulesInit()
└─ post:modules-init → full context (legacy default)

┌─ pre:ready → full context
├─ adapter.ready()
└─ post:ready → full context

Stage Contexts

Different stages have access to different parts of the application:

StageContainerModulesModuleLoaderAdapter
pre:modules-traverse
post:modules-traverse
pre:adapter-resolve
post:adapter-resolve
pre:adapter-setup
post:adapter-setup
pre:modules-init
post:modules-init
pre:ready
post:ready

Creating Plugins

Navios provides curried helper functions for creating plugins:

import { definePreAdapterResolvePlugin } from '@navios/core'

// Define your plugin factory
export const defineMyPlugin = definePreAdapterResolvePlugin({
name: 'my-plugin',
register: (context, options: { enabled: boolean }) => {
if (options.enabled) {
// Access container, modules, moduleLoader (no adapter yet)
const registry = context.container.getRegistry()
// ... your plugin logic
}
},
})

// Usage
app.usePlugin(defineMyPlugin({ enabled: true }))

Available Helper Functions

For stages without adapter access (container/modules only):

import {
definePreModulesTraversePlugin, // pre:modules-traverse
definePostModulesTraversePlugin, // post:modules-traverse
definePreAdapterResolvePlugin, // pre:adapter-resolve
} from '@navios/core'

For stages with adapter access:

import {
definePostAdapterResolvePlugin, // post:adapter-resolve
definePreAdapterSetupPlugin, // pre:adapter-setup
definePostAdapterSetupPlugin, // post:adapter-setup
definePreModulesInitPlugin, // pre:modules-init
definePostModulesInitPlugin, // post:modules-init (legacy default)
definePreReadyPlugin, // pre:ready
definePostReadyPlugin, // post:ready
} from '@navios/core'

// These require a type parameter for the adapter
export const defineMyPlugin = definePostAdapterResolvePlugin<
BunApplicationServiceInterface
>()({
name: 'my-plugin',
register: (context, options) => {
// context.adapter is typed as BunApplicationServiceInterface
},
})

Plugin Examples

Early Container Setup

Register services before modules are loaded:

import { definePreModulesTraversePlugin } from '@navios/core'
import { InjectionToken } from '@navios/di'

const ConfigToken = InjectionToken.create<Config>('Config')

export const defineConfigPlugin = definePreModulesTraversePlugin({
name: 'config',
register: (context, options: Config) => {
context.container.addInstance(ConfigToken, options)
},
})

// Usage
app.usePlugin(defineConfigPlugin({
apiUrl: 'https://api.example.com',
debug: true,
}))

Pre-Adapter Instrumentation

Modify the DI container before the adapter is instantiated (useful for OpenTelemetry):

import { definePreAdapterResolvePlugin } from '@navios/core'

export const defineOtelPlugin = definePreAdapterResolvePlugin({
name: 'otel',
register: (context, options: { serviceName: string }) => {
// Modify registry before adapter is created
const registry = context.container.getRegistry()
// Wrap or instrument services here
},
})

Post-Init Routes

Add routes after all modules are initialized (like OpenAPI):

import { definePostModulesInitPlugin } from '@navios/core'
import type { BunApplicationServiceInterface } from '@navios/adapter-bun'

export const defineDocsPlugin = definePostModulesInitPlugin<
BunApplicationServiceInterface
>()({
name: 'docs',
register: (context, options: { path: string }) => {
// Routes are registered, add documentation endpoint
context.adapter.getServer().get(options.path, () => {
return new Response('Documentation', { status: 200 })
})
},
})

Registering Plugins

Use app.usePlugin() to register plugins:

const app = await NaviosFactory.create(AppModule, {
adapter: defineBunEnvironment(),
})

// Single plugin
app.usePlugin(defineOpenApiPlugin({
info: { title: 'My API', version: '1.0.0' },
}))

// Multiple plugins in one call
app.usePlugin([
defineOtelPlugin({ serviceName: 'my-service' }),
defineOpenApiPlugin({ info: { title: 'My API', version: '1.0.0' } }),
])

await app.init()
await app.listen({ port: 3000 })

Stage Selection Guide

Choose your plugin stage based on what you need to do:

Use CaseRecommended Stage
Register global DI tokenspre:modules-traverse
Inspect loaded modulespost:modules-traverse
Instrument services before adapter creationpre:adapter-resolve
Configure adapter settingspost:adapter-resolve
Add middleware before routespre:modules-init
Generate documentation from routespost:modules-init
Log startup completepost:ready

Backward Compatibility

The legacy NaviosPlugin interface is still supported and automatically maps to the post:modules-init stage:

// Legacy pattern (still works)
const myPlugin: NaviosPlugin<{ enabled: boolean }> = {
name: 'my-plugin',
register: async (context, options) => {
// Runs at post:modules-init stage
},
}

app.usePlugin({
plugin: myPlugin,
options: { enabled: true },
})

For new plugins, we recommend using the staged plugin helpers for explicit stage control and better type safety.

Context Types

For TypeScript, you can import the context types:

import type {
ContainerOnlyContext, // pre:modules-traverse
ModulesLoadedContext, // post:modules-traverse, pre:adapter-resolve
FullPluginContext, // All stages after adapter is resolved
} from '@navios/core'