Skip to content

Route middleware is skipped when ~getMiddleware is overridden #1525

Description

@pi0x

Summary

When an app overrides ~getMiddleware, per-route middleware is silently skipped — the route handler still runs, but its own middleware never does.

Why

createDispatcher (src/h3.ts) has two paths, and they disagree about who supplies route middleware:

  • Default path — the final handler is routeHandler(route), which composes route.data.middleware in front of route.data.handler.
  • Compat path (taken when ~getMiddleware is overridden) — the final handler is the bare route?.data.handler || NoHandler, so route middleware is only present if ~getMiddleware happened to include it.

The default ~getMiddleware does include it, so an override that delegates to super is fine. But an override that returns its own list — which is what the signature invites — drops route middleware with no error.

Repro

const seen: string[] = [];
const app = new H3();
app.use(() => { seen.push("global"); });
app.get("/x", () => "ok", { middleware: [() => { seen.push("route"); }] });

// any custom ~getMiddleware that doesn't re-add route.data.middleware
(app as any)["~getMiddleware"] = function () { return this["~middleware"]; };

await app.request(new Request("http://localhost/x"));
// seen === ["global"]   — expected ["global", "route"]
// response body is still "ok", so the handler ran without its middleware

Impact

Mainly integrators that override ~getMiddleware for per-event middleware (nitro is the documented reason the branch exists). If per-route middleware is used for auth or validation, it stops being enforced while the route keeps serving.

Note

Found while reviewing route rules — not a rules bug, this reproduces with plain H3. Raising it separately since ~getMiddleware is part of the internal surface downstream depends on.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions