Skip to content

feat(openapi): endpoints.middleware to protect the spec and UI routes#4

Open
mixxtor wants to merge 1 commit into
madebyoutloud:mainfrom
mixxtor:feat/endpoints-middleware
Open

feat(openapi): endpoints.middleware to protect the spec and UI routes#4
mixxtor wants to merge 1 commit into
madebyoutloud:mainfrom
mixxtor:feat/endpoints-middleware

Conversation

@mixxtor

@mixxtor mixxtor commented Jul 25, 2026

Copy link
Copy Markdown

Body:

Closes #3

Adds an optional endpoints.middleware factory applied to both the /openapi.json and /docs routes, so the docs can be served behind authentication (e.g. middleware.auth()) instead of publicly.

// config/openapi.ts
endpoints: {
  middleware: async () => {
    const { middleware } = await import('#start/kernel')
    return [middleware.auth()]
  },
}

Design notes

  • Factory rather than a plain value — named middleware lives in #start/kernel, which cannot be imported at config-evaluation time (container services are not resolved yet; a top-level import crashes with Cannot read properties of undefined (reading 'errorHandler')). The callback defers the import until the provider resolves it.
  • Route registration moved from boot() to start() — required so the factory can import the kernel: services resolve only once the app state is booted. start() runs right after that, still before preloads are imported and before the router commits, so nothing observable changes for existing setups (verified: routes register and match exactly as before when middleware is not configured).
  • OneOrMore<T> is aliased locally in types.ts because the real one lives in @poppinss/utils/types, which is not a direct dependency of this package. Route.use() accepts exactly OneOrMore<MiddlewareFn | ParsedNamedMiddleware>.
  • A bare middleware-function value was deliberately not allowed: MiddlewareFn is itself a function, so a value | factory union cannot be discriminated at runtime.
  • Docs: new endpoints.middleware field documented in guide/configuration.

Verification

Ran the built package inside an AdonisJS 7.3 application (session web guard):

  • enabled: true + the factory above → anonymous GET /docs and GET /openapi.json redirect to the login page (302), authenticated session renders Scalar as usual.
  • enabled: true without middleware → behaves exactly as today.
  • pnpm typecheck, pnpm build, pnpm lint clean in packages/openapi.

🤖 Generated with Claude Code

Adds an optional `endpoints.middleware` factory to the config, applied to
both the /openapi.json and /docs routes, so the docs can be served behind
authentication instead of publicly.

It is a factory (rather than a plain middleware value) because named
middleware lives in #start/kernel, which cannot be imported from a config
file — config files are evaluated before container services resolve.
For the same reason, route registration moves from the provider's boot()
to start(): start() runs once the app state is 'booted' (services resolve),
and still before preloads run and the router commits, so the behavior of
existing setups is unchanged.

Verified end-to-end in an AdonisJS 7.3 app: anonymous requests to /docs and
/openapi.json get redirected by middleware.auth(), no boot-order crashes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow protecting the /docs and /openapi.json routes with middleware

1 participant