Skip to content

Register handlers in spec order#2465

Merged
mromaszewicz merged 1 commit into
oapi-codegen:mainfrom
mromaszewicz:fix/issue-1887
Jul 13, 2026
Merged

Register handlers in spec order#2465
mromaszewicz merged 1 commit into
oapi-codegen:mainfrom
mromaszewicz:fix/issue-1887

Conversation

@mromaszewicz

Copy link
Copy Markdown
Member

Closes: #1887

Route registration was emitted in sorted path order, so reordering paths in the spec had no effect. On routers that match in registration order (Fiber, Gorilla/mux) this left users no way to disambiguate overlapping paths — e.g. a request for /templates/privates/shortcuts always went to /templates/privates/{id} rather than /templates/{visibility}/shortcuts.

kin-openapi stores paths in a map, losing declaration order at parse time, so enable its origin tracking (Loader.IncludeOrigin) to recover each path's source line onto the OperationDefinition (SpecOrder). Each GenerateXServer now renders its registration template with operations sorted by SpecOrder, via a new GenerateTemplatesIntoBuffer that composes passes into one buffer (GenerateTemplates wraps it).

Model and interface generation are intentionally left in the old sorted order to preserve any existing ordering behavior — in particular the order-dependent fallback in name-collision resolution. Only handler registration follows spec order.

Closes: oapi-codegen#1887

Route registration was emitted in sorted path order, so reordering paths in
the spec had no effect. On routers that match in registration order (Fiber,
Gorilla/mux) this left users no way to disambiguate overlapping paths — e.g.
a request for `/templates/privates/shortcuts` always went to
`/templates/privates/{id}` rather than `/templates/{visibility}/shortcuts`.

kin-openapi stores paths in a map, losing declaration order at parse time,
so enable its origin tracking (`Loader.IncludeOrigin`) to recover each
path's source line onto the OperationDefinition (SpecOrder). Each
GenerateXServer now renders its registration template with operations sorted
by SpecOrder, via a new GenerateTemplatesIntoBuffer that composes passes into
one buffer (GenerateTemplates wraps it).

Model and interface generation are intentionally left in the old sorted
order to preserve any existing ordering behavior — in particular the
order-dependent fallback in name-collision resolution. Only handler
registration follows spec order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mromaszewicz
mromaszewicz requested a review from a team as a code owner July 13, 2026 18:11
@mromaszewicz mromaszewicz added bug Something isn't working notable changes Used for release notes to highlight these more highly labels Jul 13, 2026
@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes issue #1887 by making route registration follow the path declaration order in the OpenAPI spec rather than sorted alphabetical order. On routers that match in registration order (Fiber, Gorilla/mux) this gives users direct control over precedence between overlapping paths.

  • pkg/util/loader.go: enables IncludeOrigin = true so kin-openapi records each path's source line; OperationDefinition gains a SpecOrder int populated from pathItem.Origin.Key.Line.
  • pkg/codegen/operations.go: refactors all GenerateXServer functions to use the new GenerateTemplatesIntoBuffer helper, feeding sortOperationsBySpecOrder(operations) only to the registration template while leaving interface/middleware generation in the existing sorted order.
  • internal/test/servers/routeorder/: new Fiber integration test that verifies /templates/privates/shortcuts dispatches to the earlier-declared {visibility}/shortcuts route rather than the later-declared privates/{id} route.

Confidence Score: 4/5

Safe to merge with minor follow-up; the core logic is sound and well-tested, but the route-registration order change is unconditional and will cause all existing generated files to diff on the next make generate run.

The implementation is correct — sortOperationsBySpecOrder makes a defensive copy, uses a stable sort, and handles the all-zero fallback cleanly. All nine router backends are updated in parallel, the new test directory follows the prescribed layout, and the generated fixtures show the expected ordering. The two open questions are: a misplaced godoc and whether the unconditional ordering change should be gated behind a compat flag.

pkg/codegen/operations.go — the doc comment placement and the compat-flag question both live here.

Important Files Changed

Filename Overview
pkg/codegen/operations.go Core logic change: adds SpecOrder field to OperationDefinition, pathItemSourceLine helper, sortOperationsBySpecOrder, and GenerateTemplatesIntoBuffer. All GenerateXServer functions updated to pass spec-ordered ops to their registration template. One doc comment is misplaced and the unconditional behavior change warrants a compat-flag discussion.
pkg/util/loader.go Adds loader.IncludeOrigin = true to both LoadSwagger and the inner loader in LoadSwaggerWithOverlay to enable per-path source-line tracking. Straightforward and correct.
pkg/codegen/operations_spec_order_test.go New unit tests for sortOperationsBySpecOrder covering spec-line ordering, stability within a shared path, all-zero fallback, and no-mutation guarantee.
internal/test/servers/routeorder/routeorder_test.go New Fiber integration test verifying spec-order routing fix. Correctly placed under internal/test/servers/.
internal/test/servers/routeorder/spec.yaml New minimal spec with two overlapping paths ordered to exercise spec-declaration-order routing.
internal/test/servers/routeorder/routeorder.gen.go Generated Fiber server; RegisterHandlersWithOptions correctly registers TemplateShortcutGetAll before PrivateTemplateGet, matching spec declaration order.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
pkg/codegen/operations.go:1069-1074
**Misplaced godoc comment**

The line `// OperationDefinitions returns all operations for a swagger definition.` (originally the godoc for `OperationDefinitions`) is now the *first line* of `pathItemSourceLine`'s doc comment block. As a result, `go doc` will show `pathItemSourceLine` as starting with "OperationDefinitions returns all operations for a swagger definition.", and `OperationDefinitions` itself (line 1081) has no godoc at all. The `OperationDefinitions` comment line should be moved back to immediately precede `OperationDefinitions`, with a blank line separating the two function doc blocks.

### Issue 2 of 2
pkg/codegen/operations.go:1917-1924
**Unconditional behavior change — consider a compat flag**

Route-registration order now follows spec declaration order for all backends and all users, with no opt-out. The project's stated practice is that behavior-changing features be opt-in via a configuration flag (`compatibility` or `output-options`) rather than silent breaking changes. Users on order-dependent routers (Fiber, Gorilla/mux) who previously worked around the alphabetical ordering by arranging their spec paths alphabetically will silently get different runtime routing on the next code-gen run. It is worth asking whether this should be gated behind a flag (e.g. `compatibility.register-routes-in-spec-order`) so the change is opt-in rather than unconditional.

Reviews (1): Last reviewed commit: "Register handlers in spec order" | Re-trigger Greptile

Comment thread pkg/codegen/operations.go
@mromaszewicz
mromaszewicz merged commit 6574240 into oapi-codegen:main Jul 13, 2026
16 checks passed
@mromaszewicz
mromaszewicz deleted the fix/issue-1887 branch July 13, 2026 18:19
@jamietanna

jamietanna commented Jul 17, 2026

Copy link
Copy Markdown
Member

IMO, I think this may be an option we want a compatibility config option for @mromaszewicz, especially if it changes intent for everyone's specs

@mromaszewicz

Copy link
Copy Markdown
Member Author

This only affects fiber and gorilla, and only in the case where a parameterized shallow path comes before a deeper path.

For example, if you have:

  • /api/something/:param
  • /api/something/anotherthing/foo

The first is going to shadow the second.

In every other router, path registration is order independent, so it's a no-op change, just a bit of generated code churn.

I think I want this default-on, but I can add an opt out flag (even though spec editing can accomplish the same thing)

mromaszewicz added a commit to mromaszewicz/oapi-codegen that referenced this pull request Jul 17, 2026
…tion

PR oapi-codegen#2465 (issue oapi-codegen#1887) changed generated servers to register route handlers in
spec-declaration order, so that routers which match in registration order
(Fiber, Gorilla/mux) can disambiguate overlapping paths by ordering them in the
spec. This makes that behavior opt-out: the new sort-handler-registrations
compatibility flag restores the historical lexicographic (by path, then method)
registration order for users who relied on it.

The dispatch is a single seam -- operationsInRegistrationOrder picks
sortOperationsLexicographically when the flag is set, otherwise the existing
sortOperationsBySpecOrder -- and all nine GenerateXServer functions route
through it. The lexicographic sort reproduces the historical gather order
(SortedMapKeys over paths then methods), so with the flag unset the default
output is unchanged.

Adds TestSortHandlerRegistrations and documents the flag in
configuration-schema.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mromaszewicz added a commit that referenced this pull request Jul 17, 2026
…tion (#2477)

PR #2465 (issue #1887) changed generated servers to register route handlers in
spec-declaration order, so that routers which match in registration order
(Fiber, Gorilla/mux) can disambiguate overlapping paths by ordering them in the
spec. This makes that behavior opt-out: the new sort-handler-registrations
compatibility flag restores the historical lexicographic (by path, then method)
registration order for users who relied on it.

The dispatch is a single seam -- operationsInRegistrationOrder picks
sortOperationsLexicographically when the flag is set, otherwise the existing
sortOperationsBySpecOrder -- and all nine GenerateXServer functions route
through it. The lexicographic sort reproduces the historical gather order
(SortedMapKeys over paths then methods), so with the flag unset the default
output is unchanged.

Adds TestSortHandlerRegistrations and documents the flag in
configuration-schema.json.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working notable changes Used for release notes to highlight these more highly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-order generated routes

2 participants