Skip to content

Plugin middleware registration #1981

Closed
Closed

Description

@bpamiri

Summary

Plugins cannot register middleware with the middleware pipeline (#1906). A plugin that needs request-level behavior (auth checks, CORS, rate limiting) must inject into controller scopes via mixins — the wrong abstraction level.

Proposed API

Via onPluginLoad

function onPluginLoad(app) {
    app.addMiddleware("plugins.myPlugin.MyMiddleware");
}

Via plugin.json

{
  "middleware": ["AuthMiddleware", "CorsMiddleware"]
}

Behavior

  • During plugin loading, middleware declarations are collected
  • After all plugins load, declared middleware is appended to the middleware pipeline
  • Order follows plugin load order (which follows alphabetical or explicit ordering)
  • The middleware pipeline (Middleware pipeline #1906) already supports dynamic registration — this just adds the plugin integration point

Files

  • vendor/wheels/Plugins.cfc — collect middleware declarations during load
  • vendor/wheels/events/onapplicationstart.cfc — register collected middleware with pipeline

Phase

Phase 2 — DI Integration & Lifecycle (3.x, backward compatible)

Related

Activity

  1. bpamiri commented on Mar 16, 2026

    @bpamiri
    CollaboratorAuthor

    Analysis

    Synopsis

    Enable plugins to register middleware with the existing middleware pipeline (Pipeline.cfc) via onPluginLoad(app) or plugin.json, so plugins that need request-level behavior (auth, CORS, rate limiting) can use the proper middleware abstraction instead of controller-scope mixin hacks.

    Merit Assessment

    Recommendation: Proceed

    • Correct abstraction level: Request-level concerns (authentication, CORS, logging) belong in middleware, not controller mixins. Today, plugins that need this inject functions into every controller via StructAppend — this is both wasteful and semantically wrong.
    • Pipeline already supports it: Dispatch.cfc:16-37 builds the middleware pipeline from application.$wheels.middleware (or application.wheels.middleware), accepting both CFC path strings and pre-instantiated objects. Appending plugin-declared middleware to this array is trivial.
    • Clean integration point via onPluginLoad: The app.addMiddleware() API proposed in the issue maps naturally to pushing entries onto application.$wheels.middleware during the load phase.
    • plugin.json path works too: Declarative middleware in the manifest can be resolved during $pluginMetaData() and collected for later registration.

    Implementation Plan

    1. Add addMiddleware() to the app context struct (Plugin lifecycle hooks: onPluginLoad, onPluginActivate #1979's onPluginLoad(app) context). This method appends a CFC path or instance to application.$wheels.middleware:

      app.addMiddleware = function(required any middleware) {
          ArrayAppend(application.$wheels.middleware, arguments.middleware);
      };
      
    2. Collect middleware from plugin.json — In $pluginMetaData() (Plugins.cfc:184-196), if the manifest has a middleware array, resolve each entry to a full component path relative to the plugin directory and append to a variables.$class.pluginMiddleware array.

    3. Register collected middleware in onapplicationstart.cfc — After plugin loading (line 237) and before Dispatch creation (line 256), append any plugin-declared middleware to application.$wheels.middleware:

      if (StructKeyExists(application.$wheels.PluginObj, "getPluginMiddleware")) {
          ArrayAppend(application.$wheels.middleware, application.$wheels.PluginObj.getPluginMiddleware(), true);
      }
      

      This ensures plugin middleware is in the pipeline when Dispatch.$init() calls $buildMiddlewarePipeline().

    4. Ordering: Plugin middleware appends after any middleware declared in config/settings.cfm (which sets application.$wheels.middleware at line 91). Plugin load order (alphabetical by default) determines plugin middleware order. Document this clearly.

    5. Tests — Create a test plugin with middleware, verify it appears in Pipeline.getMiddleware() after bootstrap, verify its handle() is called during dispatch.

    Estimated complexity: Small (1-2 days, assuming #1979 lands first).

    Dependencies & Risks

    • Depends on Plugin lifecycle hooks: onPluginLoad, onPluginActivate #1979 (lifecycle hooks) — onPluginLoad(app) is the primary registration mechanism.
    • Soft dependency on Add plugin.json manifest support alongside box.json #1976 (plugin.json) for the declarative middleware key.
    • Bootstrap ordering is favorable: Middleware array is initialized at line 91, plugins load at line 237, Dispatch (which builds the pipeline) is created at line 256. Plugin middleware will naturally land between app-level middleware and pipeline construction.
    • Ordering control: The issue notes "order follows plugin load order" (alphabetical). For most cases this is fine, but consider adding an optional priority field for middleware that must run first/last. This can be deferred to a follow-up.
    • Route-scoped plugin middleware: The current proposal only covers global middleware. Route-scoped middleware (via mapper.scope(middleware=[...])) would require plugins to register routes first — coordinate with Plugin lifecycle hooks: onPluginLoad, onPluginActivate #1979's bootstrap ordering discussion.
    • Breaking change risk: None.

    Automated analysis by @claude — review and adjust as needed.

  2. added this to the v3.2 milestone on Mar 17, 2026
  3. bpamiri commented on Mar 26, 2026

    @bpamiri
    CollaboratorAuthor

    Implemented: $processManifestMiddleware() reads middleware array from plugin.json/package.json. Also supported via onPluginLoad(app) with app.registerMiddleware(). Tested in PluginMiddlewarePipelineSpec.cfc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions