Cordis vs Koa vs Fastify: Plugin Framework Comparison

Cordis provides a fiber-based plugin system with built-in hot-module replacement and granular lifecycle control, while Koa relies on simple middleware functions without native plugin management, and Fastify offers encapsulated plugin scopes with manual teardown hooks.

When evaluating Node.js frameworks for building modular applications, understanding the plugin architecture is critical. This comparison examines how Cordis (from the cordiverse/cordis repository), Koa, and Fastify handle plugin registration, lifecycle management, and code reloading. Unlike traditional middleware-based systems, Cordis treats plugins as first-class objects with dedicated fibers and registries, whereas Koa and Fastify take different approaches to composition and isolation.

Cordis: Fiber-Based Plugin Registry

Cordis implements a dedicated, composable plugin system built on top of a fiber-based runtime. According to the Cordis source code, the core runtime maintains a registry of plugin metadata and creates a fiber for each plugin, enabling independent lifecycle control.

Core Registry and Fiber Management

In packages/core/src/registry.ts, the Registry class tracks plugin metadata including name, entry, group flags, and configuration. When you register a plugin using ctx.plugin(plugin, config?), the system creates an isolated fiber:

// packages/core/src/registry.ts
export class Registry { 
  // Tracks plugin metadata and fiber instances
}

The loader, implemented in packages/loader/src/index.ts, resolves plugin entry points and creates fibers through the internal/plugin event:

// packages/loader/src/index.ts
ctx.on('internal/plugin', (fiber) => { 
  // Fiber creation and management logic 
})

Hierarchical and Dynamic Plugins

Cordis supports nested plugins through the group plugin (@cordisjs/plugin-group), defined in packages/loader/src/config/group.ts. For configuration-based loading, the include plugin (@cordisjs/plugin-include) watches YAML/JSON files and hot-reloads changes. The explicit HMR layer in @cordisjs/plugin-hmr watches source files and reloads fibers without restarting the application.

Koa: Middleware Stack Pattern

Koa (v2) functions as a lightweight web framework that relies on a middleware stack rather than a formal plugin system. A "plugin" in Koa is typically a function that returns middleware:

app.use(async (ctx, next) => {
  await next()
})

Koa does not provide a built-in plugin registry, scoping mechanism, or lifecycle hooks. Developers must manually manage registration order by pushing functions onto the stack, and hot-reloading requires external tools like nodemon. Because middleware are simple functions, there is no explicit enable/disable lifecycle beyond adding or removing functions from the stack.

Fastify: Encapsulated Plugin Scopes

Fastify ships with a structured plugin system that creates encapsulation scopes for each registered plugin. Plugins are functions that receive a Fastify instance and options object, registered via fastify.register(plugin, opts):

await app.register(plugin, { prefix: '/api' })

Each registration creates an isolated instance with its own decorators, hooks, and routes. Fastify provides the fastify-plugin helper for autoinjecting dependencies across scopes and supports lifecycle hooks like onClose for graceful teardown. However, hot-module-replacement is not native; developers typically rely on external tools such as fastify-cli with ts-node-dev.

Feature Comparison

Plugin Registration

  • Cordis: Uses ctx.plugin(plugin, config?) with a centralized registry tracking all metadata and fiber states.
  • Koa: Pushes middleware functions onto a stack via app.use(fn) with no registry or metadata tracking.
  • Fastify: Uses fastify.register(plugin, opts) to create encapsulated scopes with isolated state.

Lifecycle Control

  • Cordis: Supports enable, disable, dispose, and HMR operations through fiber management.
  • Koa: Requires manual stack manipulation; no built-in lifecycle hooks for activation or deactivation.
  • Fastify: Provides onClose and onRegister hooks, but teardown is largely manual.

Hot Module Replacement

  • Cordis: Built-in HMR via @cordisjs/plugin-hmr watches files and reloads individual fibers.
  • Koa: No native support; requires external process managers.
  • Fastify: No native support; relies on external development tools.

Dependency Injection

  • Cordis: Plugins declare an inject array; the loader resolves dependencies automatically.
  • Koa: No DI system; dependencies shared via closure or external containers.
  • Fastify: fastify-plugin exposes decorators for later plugins to consume.

Scope Isolation

  • Cordis: Fibers isolate state per plugin; sharing occurs through the shared context.
  • Koa: Uses a shared ctx object with no isolation between middleware.
  • Fastify: Encapsulation scopes isolate decorators, hooks, and routes per plugin.

Implementation Examples

Cordis Plugin Registration

In packages/timer/tests/index.spec.ts, the timer plugin demonstrates how Cordis handles configuration and events:

import { Context } from '@cordisjs/core'
import Timer from '@cordisjs/plugin-timer'

async function main() {
  const ctx = new Context()
  await ctx.plugin(Timer, { interval: 1000 })
  ctx.on('timer', (msg) => console.log('tick:', msg))
}
main()

Koa Middleware Pattern

Standard Koa usage involves adding logging or utility middleware directly:

const Koa = require('koa')
const app = new Koa()

app.use(async (ctx, next) => {
  console.log('Request start')
  await next()
  console.log('Request end')
})

app.listen(3000)

Fastify Encapsulated Plugin

Using fastify-plugin to expose decorators within isolated scopes:

import fastify from 'fastify'
import fp from 'fastify-plugin'

const plugin = fp(async (instance, opts) => {
  instance.decorate('custom', () => 'hello')
  instance.get('/hello', (req, reply) => reply.send(instance.custom()))
})

async function start() {
  const app = fastify()
  await app.register(plugin)
  await app.listen(3000)
}
start()

Key Source Files in Cordis

The Cordis architecture is implemented across several packages:

Summary

  • Cordis provides a fiber-based runtime with a central registry, built-in HMR, and granular lifecycle control for complex, long-running services.
  • Koa offers a lightweight middleware stack without native plugin management, requiring external tools for reloading and manual composition.
  • Fastify delivers encapsulated scopes with decorator isolation and lifecycle hooks, though HMR requires external development tools.
  • Cordis's dependency injection and group/include plugins enable hierarchical, configuration-driven architectures not native to Koa or Fastify.
  • For applications requiring hot-reloadable modules and fine-grained plugin control, Cordis provides capabilities that extend beyond Fastify's encapsulation or Koa's middleware pattern.

Frequently Asked Questions

What is the main architectural difference between Cordis and Fastify plugins?

Cordis implements a fiber-based architecture where each plugin runs in its own fiber with independent lifecycle management, while Fastify uses encapsulation scopes that isolate decorators and routes but run within the same process context. Cordis tracks all plugins in a central registry (packages/core/src/registry.ts) with metadata, whereas Fastify relies on function-based registration without a central metadata store.

Does Koa support hot module replacement like Cordis?

No, Koa does not provide native HMR. As a middleware-based framework, Koa requires you to add or remove functions from the stack manually, and any hot-reloading must be handled by external process managers like nodemon or ts-node-dev. In contrast, Cordis provides @cordisjs/plugin-hmr, which watches source files and performs granular fiber disposal and reloading without restarting the entire application.

How does Cordis handle plugin dependencies?

Cordis supports explicit dependency injection through the inject array property on plugin objects. The loader in packages/loader/src/index.ts resolves these dependencies before instantiating the plugin fiber. Fastify achieves similar functionality through fastify-plugin to break encapsulation, while Koa has no built-in DI mechanism, forcing developers to share state via closures or external containers.

Which framework is best for building modular, long-running services?

Cordis is designed specifically for modular, long-running services that require fine-grained lifecycle control, hot module replacement, and hierarchical plugin organization. Fastify suits high-performance web APIs with its encapsulation model, while Koa remains ideal for lightweight middleware pipelines where minimal overhead is prioritized over plugin management features.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →