Skip to content

🔥 feat: Add OpenAPI middleware#3702

Draft
gaby wants to merge 39 commits into
mainfrom
2025-08-21-14-48-18
Draft

🔥 feat: Add OpenAPI middleware#3702
gaby wants to merge 39 commits into
mainfrom
2025-08-21-14-48-18

Conversation

@gaby

@gaby gaby commented Aug 21, 2025

Copy link
Copy Markdown
Member

Description

This PR introduces an OpenAPI middleware that auto-generates OpenAPI 3.0 specifications from registered Fiber routes. The middleware provides comprehensive support for documenting APIs through both fluent route methods and middleware configuration, making it easy to maintain up-to-date API documentation.

Changes introduced

  • OpenAPI Middleware Package: New middleware that automatically generates OpenAPI 3.0 JSON specifications from your Fiber application routes

  • Route Metadata Support: Extended the Route struct with OpenAPI-specific fields including Summary, Description, Tags, Parameters, RequestBody, Responses, Consumes, Produces, and Deprecated

  • Fluent API Methods: Added chainable methods to App, Group, and domainRouter for documenting routes inline (e.g., .Summary(), .Description(), .Tags(), .Parameter(), .Response(), .RequestBody())

  • Schema References: Support for OpenAPI schema references ($ref) and examples at the parameter, request body, and response levels

  • Auto-filtering: Automatically filters out Fiber's auto-generated HEAD routes (via Route.IsAutoHead()) and middleware routes registered with Use() (via Route.IsMiddleware()) to avoid cluttering the spec with synthetic operations

  • Route Introspection Methods: Added IsMiddleware() and IsAutoHead() public methods on Route to allow middleware and external consumers to distinguish middleware/auto-generated routes from user-defined routes

  • Flexible Configuration: Per-route metadata can be provided via fluent API or global middleware config (keyed by Fiber route syntax, e.g. GET /users/:id), with config taking precedence

  • Explicit Request Body Suppression: A non-nil config RequestBody with an empty Content map is treated as an explicit "no request body" override, preventing the default auto-insertion for POST/PUT/PATCH methods

  • Group Support: Correctly handles grouped routes and mounted sub-apps with proper path resolution

  • Domain Router Support: All OpenAPI fluent methods are implemented on domainRouter, ensuring domain-scoped routes can be documented identically to standard routes

  • Safe Route Cloning: copyRoute() deep-clones all OpenAPI-related fields including Tags, Parameters, Responses, and RequestBody to prevent shared backing arrays between mounted/cloned apps

  • Immutable Route Metadata: App.Tags() defensive-copies the incoming variadic slice before storing, preventing caller-side mutations from affecting route metadata

  • OpenAPI Spec Validity: buildRequestBody() omits the request body entirely when content is empty, preventing invalid OpenAPI documents with "content":null

  • Merge Conflict Fixes: Resolved duplicate field declarations in Route struct, handler type conversion issues, semantic conflicts in test files, and integrated parallel benchmark tests from main branch

  • Code Quality Improvements: Fixed all lint issues (deprecated utils.ToLower replaced with utilsstrings.ToLower, 28 httpNoBody warnings, 5 whyNoLint warnings, 4 paramTypeCombine warnings, 2 hugeParam warnings), applied struct alignment optimizations (reduced Operation struct from 136 to 128 bytes, Media struct from 48 to 40 bytes), and ensured code passes all quality checks with 0 issues

  • Security Hardening:

    • Input Validation: Consumes() and Produces() now trim whitespace before validation, preventing unexpected panics from inputs like " application/json" or trailing spaces
    • OpenAPI Path Template Generation: Implemented convertToOpenAPIPath() function that properly converts Fiber route patterns to valid OpenAPI path templates by stripping type constraints (:id<int>{id}), handling regex constraints, converting wildcards (* and +{wildcard}), and skipping optional markers (?)
    • Nil Pointer Protection: Added defensive nil check in appendOrReplaceParameter() to prevent potential runtime panics if code is refactored
    • Bounds Checking: All array/string indexing operations in convertToOpenAPIPath() properly guarded with length checks to prevent index out of bounds errors
    • Comprehensive Testing: Added 9 test cases covering simple paths, parameters with constraints, regex constraints, optional parameters, wildcards, plus params, multiple parameters, and various delimiters
  • Documentation Improvements:

    • Caching Behavior: Added explicit documentation explaining that the OpenAPI spec is generated once on the first matching request and cached for the process lifetime, warning users to register the middleware after all routes
    • Markdown Compliance: All documentation properly formatted and passing markdown linting with 0 errors
  • Test Coverage Improvements: Comprehensive test suite with 93.1% code coverage (exceeding 90% goal)

    • Added 10 new test functions covering request body merge scenarios, media content defaults, path resolution edge cases, parameter merging, schema handling, HTTP method logic, nil parameter handling, marshal errors, and empty media types
    • All tests use t.Parallel() for concurrent execution
    • Per-function coverage improvements: mergeConfigParameters (76.9% → 92.3%), buildRequestBody (58.8% → 94.1%), schemaFrom (70.0% → 90.0%), shouldIncludeRequestBody (77.8% → 88.9%), resolvedSpecPath (70.6% → 82.4%), convertMediaContent (63.2% → 78.9%)
  • Benchmarks: No performance impact as spec generation happens once on first request via sync.Once. Merged 17 parallel benchmark tests from main branch to ensure thread-safety of router operations.

  • Documentation Update: Added comprehensive documentation at docs/middleware/openapi.md with examples and configuration options. Operations key format clarified to use Fiber route syntax (e.g. GET /users/:id). Added explicit caching behavior warnings. All markdown properly formatted and passing linting.

  • Changelog/What's New: OpenAPI middleware enables automatic API documentation generation from route definitions. Default responses documented as 200 OK for most methods, 204 No Content for DELETE and HEAD. Properly handles Fiber route constraints and wildcards in generated OpenAPI paths.

  • Migration Guide: No migration needed - this is a new opt-in middleware

  • API Alignment with Express: Not applicable - OpenAPI specification is framework-agnostic

  • API Longevity: The middleware uses OpenAPI 3.0 standard with extensible configuration structures to accommodate future enhancements. Security hardening ensures production stability.

  • Examples: Documentation includes examples for basic usage, custom metadata, schema references, grouped routes, and proper middleware registration order

Type of change

  • New feature (non-breaking change which adds functionality)
  • Code consistency (non-breaking change which improves code reliability and robustness)
  • Performance improvement (non-breaking change which improves efficiency)

Checklist

  • Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage.
  • Conducted a self-review of the code and provided comments for complex or critical parts.
  • Updated the documentation in the /docs/ directory for Fiber's documentation.
  • Added or updated unit tests to validate the effectiveness of the changes or new features.
  • Ensured that new and existing unit tests pass locally with the changes.
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community.
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.
  • Completed comprehensive security audit to prevent runtime panics and ensure production stability.
  • Achieved 93.1% test coverage with comprehensive test suite covering all edge cases.

📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

@coderabbitai

coderabbitai Bot commented Aug 21, 2025

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 24df09d2-603a-4b8e-99ae-77146488734a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 2025-08-21-14-48-18

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gaby gaby changed the title feat: add openapi middleware 🔥 feat: Add OpenAPI middleware Aug 21, 2025
@gaby gaby added the v3 label Aug 21, 2025
@gaby gaby added this to v3 Aug 21, 2025
@gaby gaby added this to the v3 milestone Aug 21, 2025
@codecov

codecov Bot commented Aug 21, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.03071% with 89 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.48%. Comparing base (981cf15) to head (be34c4a).

Files with missing lines Patch % Lines
app.go 87.13% 28 Missing and 16 partials ⚠️
middleware/openapi/openapi.go 96.12% 16 Missing and 10 partials ⚠️
router.go 94.19% 7 Missing and 6 partials ⚠️
middleware/openapi/schema.go 96.96% 5 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3702      +/-   ##
==========================================
+ Coverage   93.23%   93.48%   +0.25%     
==========================================
  Files         140      143       +3     
  Lines       14567    16292    +1725     
==========================================
+ Hits        13581    15230    +1649     
- Misses        614      664      +50     
- Partials      372      398      +26     
Flag Coverage Δ
unittests 93.48% <95.03%> (+0.25%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gaby gaby moved this to In Progress in v3 Aug 21, 2025
@gaby

gaby commented Aug 21, 2025

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@gaby

gaby commented Aug 21, 2025

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new OpenAPI middleware for auto-generating API specifications. The implementation is solid, with good test coverage and documentation. I've identified a potential improvement to prevent the middleware from documenting its own endpoint in the generated spec, which would make the output cleaner for API consumers. I also found a minor formatting issue in the documentation. Overall, this is a great feature addition.

Comment thread docs/middleware/openapi.md Outdated
Comment thread middleware/openapi/openapi.go Outdated
@ReneWerner87

Copy link
Copy Markdown
Member

nice feature, thx @gaby
can you update and convert the DRAFT to READY (when it is ready for you)

@gaby
gaby requested a review from Copilot October 25, 2025 16:44
@gaby

gaby commented Oct 25, 2025

Copy link
Copy Markdown
Member Author

/gemini review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces an OpenAPI middleware that auto-generates OpenAPI 3.0 specifications from registered Fiber routes. The implementation adds per-route metadata capabilities (summary, description, tags, parameters, request/response bodies, deprecation) that can be configured either through fluent route builders or global middleware configuration.

Key changes:

  • New OpenAPI middleware package with spec generation and JSON serving
  • Extended Route struct with OpenAPI-specific metadata fields
  • Added fluent API methods to both App and Group for route documentation

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
middleware/openapi/openapi.go Core middleware implementation generating OpenAPI spec from routes
middleware/openapi/config.go Configuration types for middleware and operation metadata
middleware/openapi/openapi_test.go Comprehensive test suite covering spec generation scenarios
middleware/openapi/testdata/openapi.json Golden file fixture for JSON output validation
router.go Extended Route struct with OpenAPI fields and helper types
router_test.go Tests for new route metadata methods
app.go Fluent API methods for documenting routes
group.go Group delegation methods for route documentation
group_test.go Tests for group-level route documentation
docs/middleware/openapi.md User-facing middleware documentation
docs/whats_new.md Release notes entry

@gaby

gaby commented Oct 25, 2025

Copy link
Copy Markdown
Member Author

@codex review and suggest feature improvements.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new OpenAPI middleware to auto-generate API specifications from routes. The implementation is comprehensive, adding new methods to App and Group for route-level documentation, and providing configuration options for global overrides. The code is well-structured and includes extensive tests.

I have two main points of feedback. The most critical one is that the current implementation incorrectly includes middleware routes (from app.Use) in the generated OpenAPI spec, which should be fixed to ensure a clean and accurate API definition. The other is a minor improvement to an error message for better debugging. Overall, this is a great feature addition.

Comment thread middleware/openapi/openapi.go Outdated
Comment thread router.go Outdated
Comment thread app.go Outdated
@gofiber gofiber deleted a comment from chatgpt-codex-connector Bot Oct 25, 2025
@gaby

gaby commented Oct 25, 2025

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread middleware/openapi/openapi.go
@ReneWerner87

Copy link
Copy Markdown
Member

@gaby can you update this PR

@gaby

gaby commented Mar 27, 2026

Copy link
Copy Markdown
Member Author

Fixing conflicts this weekend

Copilot AI and others added 27 commits July 18, 2026 03:13
…clusivity per OpenAPI spec

Agent-Logs-Url: https://github.com/gofiber/fiber/sessions/15c9fc8c-eb8e-4dd2-a0f5-cb6a56c6c9c3

Co-authored-by: gaby <835733+gaby@users.noreply.github.com>
… inference

Add SchemaOf() function that generates OpenAPI JSON Schema from Go structs
using reflection. Supports all primitive types, nested structs, embedded
structs, slices, maps, time.Time, pointers, and custom openapi struct tags
for description, example, format, and enum values.

Includes 19 schema tests and 1 integration test. All lint checks pass.

Agent-Logs-Url: https://github.com/gofiber/fiber/sessions/50af7681-fa58-4657-8390-570ea9fa0316

Co-authored-by: gaby <835733+gaby@users.noreply.github.com>
…caching

Address audit gaps in the OpenAPI middleware so it produces a more complete,
production-grade specification out of the box:

- Security: add Config.SecuritySchemes (emitted under
  components.securitySchemes) and document-level Config.Security, plus a
  route-level Security(...) helper for per-operation requirements.
- Spec completeness: add info.contact/license/termsOfService, multiple
  Servers (with ServerURL back-compat), top-level Tags, and ExternalDocs.
- Caching: regenerate the cached spec when the registered route count
  changes so routes added after the first request are reflected.
- Swagger UI: load the standalone preset and use StandaloneLayout by
  default (Authorize button), and tolerate trailing slashes on the spec/UI
  paths.

Includes tests and docs for all of the above.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
- Introduce constants for repeated schema literals ($ref, type, format,
  string, object, path) in the fiber and openapi packages (goconst).
- Reorder Config and openAPISpec struct fields for optimal alignment
  (govet fieldalignment), preserving field doc comments.
- Drop the always-constant path parameter from the fetchJSON test helper
  (unparam).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Replace strconv.ParseInt/ParseFloat with the gofiber/utils/v2 equivalents
(utils.ParseInt, utils.ParseFloat64) in inferExampleValue, matching the
repo convention of preferring Fiber utils. ParseBool stays on strconv as
utils has no equivalent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Replace strings.TrimSpace with utils.TrimSpace in schema.go and openapi.go,
and convert the case-insensitive location comparison in remapRouteParameters
to utils.EqualFold (dropping the ToLower allocation). Normalization that is
stored keeps utilsstrings.ToLower for consistency. strings stays for the
split/cut/contains helpers that have no fiber-utils equivalent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
schema.go (SchemaOf):
- Add cycle detection so self-referential / mutually-recursive structs no
  longer stack-overflow; the cycle point emits a bare {"type":"object"}.
- Marshal []byte as {"type":"string","format":"byte"} (Go encodes it base64);
  fixed-size byte arrays stay number arrays.
- Flatten embedded pointers to structs (like encoding/json); embedded-pointer
  fields are not marked required since the embed may be nil.
- interface{}/any -> {} (any value); skip fields with no JSON representation
  (chan, func, complex, ...) instead of emitting a meaningless {}.
- Parse openapi struct tags with a directive-aware regexp so values may
  contain commas and colons.

openapi.go (generateSpec):
- Emit a unique, non-empty operationId for every operation (generated from
  method+path when a route has no Name; deduped with numeric suffixes).
- Auto-generated summary uses the OpenAPI path template ({id}) instead of raw
  Fiber syntax (:id<int>).

Adds tests for all of the above and updates the docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
…hardening

Features:
- Hidden() route helper (Router/App/Group/domainRouter) and Route.IsHidden()
  exclude a route from the generated spec; generateSpec skips hidden routes.
- ResponseHeader(status, name, description, schema) documents response headers,
  emitted under responses.<code>.headers; RouteResponse/response carry Headers
  and it is cloned in copyRoute.

Hardening:
- GET and HEAD operations never emit a requestBody (explicit or implicit).
- The cached-spec route count is now read inside the mutex, so the count
  compared and the spec generated are always consistent.

Refactor defaultResponseDescription/defaultResponseKey shared by the response
helpers. Adds tests and docs for all of the above.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Add a dependency-free structural validator (validate_test.go) that asserts the
OpenAPI 3.0/3.1 invariants the middleware must satisfy: required info, valid
operation methods, path-template params declared, non-empty responses with
descriptions, globally-unique operationIds, valid/unique parameters, non-empty
requestBody content with no body on GET/HEAD, and security requirements that
reference defined securitySchemes. Runs over a feature-exercising app for both
supported OpenAPI versions as a regression guard. Test-only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Break the single large example blob into one titled subsection per topic
(Import, Quick start, Document metadata, Authentication, Customize the Swagger
UI, Document a route, Per-operation security, Response headers, Hide a route,
Behavior and defaults), each with its own code block, and split the SchemaOf
example into struct/route-helper/Components blocks. Content is unchanged;
markdownlint passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Parameter serialization: RouteParameter gains deprecated/style/explode/
allowEmptyValue/allowReserved, exposed via a new AddParameter(RouteParameter)
helper (Parameter/ParameterWithExample now build on it).

Metadata completeness: Server.Variables (+ ServerVariable), Tag.ExternalDocs,
Config.Summary (info.summary), and operation-level OperationExternalDocs.

Responses & content: per-media-type request/response bodies via RouteMediaType +
RequestBodyContent/ResponseContent (different schema/example/encoding per content
type), and response links via ResponseLink.

Advanced 3.1: Config.Webhooks, Config.JSONSchemaDialect, and an OperationExtension
helper that shallow-merges arbitrary operation fields (servers, callbacks, x-*)
without clobbering generated keys. The 3.1-only document fields (summary,
webhooks, jsonSchemaDialect) are emitted only for OpenAPIVersion 3.1.0.

New Router methods are implemented on App and delegated by Group/domainRouter;
all new Route fields are deep-cloned in copyRoute. Includes tests for every
feature (extending the structural validator app) and full docs sections.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
- Accept "3.2.0" as an OpenAPIVersion (default stays 3.1.0).
- Add an ordered versionAtLeast() check; emit 3.1-era fields (summary,
  webhooks, jsonSchemaDialect) for 3.1 AND 3.2 (previously dropped for 3.2),
  and 3.2-only fields for >= 3.2.
- QUERY method: routes register via app.Query() (from main); the middleware
  emits them as the OpenAPI `query` operation for 3.2, and skips them for
  3.0/3.1 where the operation key does not exist. QUERY keeps its request body.
- New typed 3.2 fields: root `$self` (Config.Self), Server.Name, and
  License.Identifier (SPDX, 3.1+); each gated to its supporting version.
- Allow the 3.2 `querystring` parameter location in AddParameter.

Tag summary/parent/kind were intentionally NOT added: the 3.2 Tag Object only
defines name/description/externalDocs. Other 3.2 additions (security device
flow/oauth2MetadataUrl, XML nodeType, Media Type itemSchema, additionalOperations,
components.mediaTypes) are supplied via the existing raw-map Components/schemas.

Adds tests (version gating, QUERY gating, querystring location), extends the
structural validator to cover `query`/`querystring` and a 3.2 case, and documents
all of the above.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Add coverage_test.go exercising the internal helper edge branches that the
black-box tests don't reach: uniqueOperationID fallback/suffix, schemaFrom,
mediaTypesToContent/routeMediaTypeContent empties, buildRequestBody guards,
shouldIncludeRequestBody, defaultResponseForMethod, buildServers (empty-URL skip,
name gating, ServerURL fallback), buildComponents securityScheme merge,
mergeRouteParameters (blank name, default location), remapRouteParameters
(unknown path param dropped), sanitize/resolve param-name helpers,
buildOpenAPIPathVariants edge cases, inferExampleValue, and SchemaOf with
unsupported slice/map element types. Plus two black-box error paths
(spec-marshal and swagger-options-marshal failures) via app.Test.

Statement coverage 92.8% -> 98.6%. The remaining lines are unreachable defensive
guards (JSON marshal/unmarshal errors on known-good structs, the swagger template
execute error, resolvedSpecPath empty-path/nil-route guards, the
applyOpenAPITag/convertToOpenAPIPath dead branches).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
…a helpers

- Restrict applyToLatestRouteLocked to routes created by the same
  registration so Use()/GET metadata no longer clobbers concrete or
  explicitly registered HEAD routes
- Match spec/UI paths with the app's case sensitivity setting
- Regenerate the spec per request instead of caching by route count,
  fixing stale specs after route removal and cross-app cache reuse
- Cache Swagger UI pages per mount prefix so multiple prefixes get
  correct spec URLs
- Derive the mount prefix only for middleware routes so the handler
  works when registered on exact method routes
- Keep the first registered operation when path variants collide,
  matching router dispatch precedence
- Handle escaped characters in route paths when building OpenAPI paths
- Make parent struct fields shadow embedded fields in SchemaOf and
  dedupe required entries, matching encoding/json
- Merge Response() with previously documented headers/links and stop
  overwriting explicit Consumes/Produces values
- Reuse shared helpers (JSONEncoder, utils.StatusMessage, utils
  string helpers, single media-type validator), name the path walker
  state type, move convertToOpenAPIPath into tests, add omitempty to
  new Route JSON fields, and reduce redundant copying in AddParameter

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wf9hoGmGf9bnbqYZA8xa39
- Identify routes of one registration by a registration ID stamped in
  register(), so metadata helpers reach exactly the entries created (or
  compression-merged) by the latest registration and never clobber
  shadowed duplicates, other Use() registrations, or domain routes;
  Name() now shares the same targeting
- Track mount routes as the latest registration so helpers chained onto
  app.Use(prefix, subApp) no longer mutate the previously registered route
- Re-derive Route.Params when mount expansion prefixes a route so
  parameterized mount prefixes produce correct OpenAPI path parameters
- Resolve concrete mount prefixes from the request path for parameterized
  Use() prefixes, and derive the prefix from the configured suffix when
  the handler is registered on exact routes (including inside mounted
  sub-apps)
- Fall back to the route's Produces media type when a response documents
  a schema or example without media types instead of dropping it
- Drop OpenAPI 3.2-only querystring parameters from 3.0/3.1 documents and
  stop forcing a default schema on them
- Preserve nil elements when deep-copying typed slices and maps in route
  metadata instead of panicking or dropping keys
- SchemaOf: honor the json ",string" option, drop fields promoted
  ambiguously by multiple embedded structs, and make the required list
  deterministic
- Cleanups: bound the Swagger UI page cache, avoid per-request config
  copies and allocations on the middleware hot path, shared responseKey
  helper, maps.Clone instead of a local copy helper, simplify Tags and
  cloneRouteParameters

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wf9hoGmGf9bnbqYZA8xa39
- Stamp one registration ID per register() call (not per method) so
  helpers chained on a multi-method Add document every method's route
- Clear registration IDs when mount expansion clones sub-app routes into
  the parent stack, preventing cross-counter collisions that let later
  parent registrations clobber mounted-route metadata
- Resolve exact-route spec/UI targets from the request path so handlers
  registered on exact routes work under parameterized mount prefixes
- SchemaOf: resolve fields level by level like encoding/json (shallowest
  depth wins, single tagged candidate dominates, ambiguous names dropped),
  promote exported fields of embedded unexported structs, and type enum
  values to match the field's schema type
- Skip the default string schema for querystring parameters at spec
  generation as well
- Cleanups: remove the double handler conversion in Use(), reuse the
  exported Server/Tag types in the spec document, drop redundant deep
  copies in RequestBodyContent/ResponseContent/ResponseHeader, make the
  querystring filter allocation-free when nothing matches, delete a
  pass-through sanitize wrapper, and use maps.Clone in pathState.clone

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wf9hoGmGf9bnbqYZA8xa39
…back

- Types implementing json.Marshaler (including structs promoting one from
  an embedded type such as time.Time) are documented as accepting any
  value, and encoding.TextMarshaler types as strings, since encoding/json
  bypasses field reflection for them
- Remove the unreachable empty-path fallback in normalizedPath;
  configDefault already applies path defaults

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wf9hoGmGf9bnbqYZA8xa39
…ointer receivers

- json.Number is a string kind but marshals as a bare JSON number, so
  document it as {"type": "number"}
- A pointer-receiver-only TextMarshaler cannot be invoked on
  non-addressable values, making the wire shape unknowable; document such
  types as accepting any value, reserving {"type": "string"} for
  value-receiver implementations

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wf9hoGmGf9bnbqYZA8xa39
- Distinct Fiber parameters that sanitize to the same identifier now get
  numeric suffixes in the generated path template, since OpenAPI forbids
  duplicated path parameter names
- Correct the OpenAPIVersion doc comment to list 3.2.0 and the Webhooks
  comment to say 3.1.0 and above

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wf9hoGmGf9bnbqYZA8xa39
- Add a routesRevision counter (bumped on route add/remove, metadata mutation,
  auto-HEAD pruning/creation, and mount flattening) with an exported
  App.RoutesRevision(), and make GetRoutes take deep copies under app.mutex so
  consumers can read route metadata without racing registration.
- applyToLatestRouteLocked now applies to every route created by the most
  recent registration (Add/All document all method variants, not just the
  last) and a use-route's metadata no longer leaks onto unrelated endpoints
  that share its path.
- register() no longer stamps Consumes/Produces with text/plain; empty means
  "unspecified" so generated specs stop inventing request/response media types.
- copyCompositeValue handles nil interface elements inside typed slices/maps
  instead of panicking with "reflect: Set using zero Value".
- Auto-HEAD twins use copyRouteBase and skip the deep clone of documentation
  metadata that is never read.
- Register/RouteChain gains the full set of documentation helpers (Registering
  and domainRegistering delegators), matching Router/Group/domainRouter.
- Extract validateResponseStatus/responseKey/getOrCreateResponse shared by the
  response helpers.
- Add regression tests incl. a reflective clone-completeness gate that fails
  when a new Route field is not wired into copyRoute.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
- Spec generation consumes App.GetRoutes() deep snapshots and invalidates on
  App.RoutesRevision(), eliminating the data race between /openapi.json
  requests and concurrent route registration/documentation, and refreshing the
  cached spec on metadata-only changes.
- Honest defaults: no implicit request body unless Consumes/RequestBody is set
  explicitly, default responses are description-only, and TRACE joins GET/HEAD
  in never emitting a requestBody (RFC 9110). shouldIncludeRequestBody's dead
  method switch is gone; the generateSpec strip is the single method rule.
- resolvedSpecPath strips the configured path suffix from the mount prefix, so
  exact-route mounts (app.Get("/openapi.json", New())) serve instead of 404ing.
- Path params whose sanitized names collide get numeric suffixes, keeping
  parameter names unique per path (valid OpenAPI).
- Resolved spec/UI targets are cached per mount behind an atomic pointer
  instead of being recomputed on every GET/HEAD request.
- Cleanups: delete dead convertToOpenAPIPath, rename the shallow copyAnyMap to
  shallowCopyMap to distinguish it from the fiber package's deep variant, and
  drop the openAPIServer/openAPITag mirror types in favor of Server/Tag.
- Regression tests for the race (-race), exact mounts, collision uniqueness,
  and metadata freshness after first serve; docs updated for the new defaults,
  exact mounts, and RouteChain documentation helpers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Locking:
- ShutdownWithContext no longer holds app.mutex across the server shutdown
  wait, so in-flight handlers calling GetRoutes cannot deadlock shutdown.
- OnRoute/OnName hooks fire after the router lock is released (with private
  route snapshots), so hooks may call GetRoutes, documentation helpers, or
  RemoveRoute without self-deadlocking.
- GetRoute now locks and deep-clones like GetRoutes, so the returned metadata
  maps never alias live router state.
- processSubAppsRoutes clones sub-app routes under the sub-app's mutex,
  fixing a data race with concurrent sub-app documentation helpers.

Registration cursor:
- register() returns its registration ID; Group, Registering, domainRouter,
  and domainRegistering capture it so their documentation helpers target
  their own last registration instead of the app-global latest route. The
  per-helper mutations now live in shared doc* factories instead of five
  copies of each helper body.
- Chained helpers apply to the current registration batch in O(batch); the
  full stack scan remains only for older scoped handles.
- The handler-merge branch no longer restamps the merged entry's regID and
  points latestRoute at the live stack entry instead of a discarded ghost;
  merged-entry documentation deliberately belongs to the latest registration.
- Same-path routes on different domains are never merged: routes carry their
  domain pattern and the merge condition compares it, so each domain keeps
  its own handlers and documentation.
- deleteRoute invalidates the cursor and batch; auto-HEAD generation no
  longer repoints latestRoute, so stray post-startup helpers cannot
  re-document an arbitrary route. Mount placeholders are explicit no-ops for
  doc helpers (their metadata used to vanish at startup).

Clone fidelity:
- cloneRouteSecurity keeps empty scope lists non-nil so the generated spec
  emits [] instead of null.
- Example fields are deep-copied via copyAnyValue like Examples, closing the
  aliasing hole in GetRoutes snapshots.
- Auto-HEAD twins are uniformly undocumented (scalar doc fields blanked)
  instead of half-copied.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
…path hierarchies

- Scope the spec and Swagger UI caches per *fiber.App: a handler value shared
  across apps can no longer serve one app's cached spec to another, and the
  case-sensitivity setting is read per request instead of latched from the
  first app. The spec is cached once per app (it never depended on the target
  path), removing duplicate cache entries and the permanent regeneration
  cliff behind the old 32-target bound.
- routePrefix classifies mount-prefix segments by routing tokens only:
  '*'/'+' inside a <constraint> (e.g. :id<regex([0-9]+)>) or behind a '\'
  escape are literals, so the spec and UI stay reachable under such mounts;
  static prefixes are served in their unescaped request form.
- generateSpec emits one templated path per hierarchy level and method:
  multi-optional-parameter routes no longer produce sibling templates that
  differ only in parameter name, which the OpenAPI spec forbids.
- The Swagger UI options are marshaled with the app's configured JSONEncoder,
  matching the spec encoding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
…EAD change

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Rebasing the branch onto main replayed the PR's commits with a
prefer-the-commit conflict strategy; this restores the main-side content
that strategy overwrote (the SkipUnmatchedRoutes router tests, router match
signature and app shutdown-hook updates, workflow/Makefile changes) plus the
branch's own domain/group scoped-helper tests dropped in the same way. The
resulting tree is identical to a clean merge of main into the branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
@gaby
gaby force-pushed the 2025-08-21-14-48-18 branch from feb2c8a to a75ddff Compare July 18, 2026 03:18
@gaby gaby removed the codex label Jul 18, 2026
…e-chain

The domainRouter, domainRegistering, Registering, and Group documentation
helpers were almost entirely untested (31 domain.go delegators, all 20
Registering helpers, and 9 Group helpers reported at 0% coverage). Add:

- Test_Domain_OpenAPI_Helpers_Advanced: the domainRouter helpers not already
  covered (Security, Hidden, ResponseHeader, AddParameter,
  OperationExternalDocs, RequestBodyContent, ResponseContent, ResponseLink,
  OperationExtension).
- Test_Domain_RouteChain_OpenAPI_Helpers: the full domainRegistering surface
  via a domain-scoped RouteChain, with request-body variants split across
  registrations so each is asserted rather than clobbered.
- Test_RouteChain_OpenAPI_Helpers / Test_RouteChain_Nested: the Registering
  fluent API and nested chain prefixing.
- Test_Group_OpenAPI_Helpers_Advanced: the untested Group helpers.
- Domain routing edge cases: empty-handler Use under a domain group, the
  invalid-handler panic, and the OnGroup/OnMount hook-error panics.

domain.go drops from 31 zero-coverage functions to none; the only line left
uncovered is an unreachable defensive panic (maxParams is guarded by the
smaller maxDomainParts limit first).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XHncVMdy1VPWCTp53PQoYG
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

6 participants