support optional/nullable response headers#2301
Merged
Merged
Conversation
jamietanna
reviewed
Mar 25, 2026
Member
jamietanna
left a comment
There was a problem hiding this comment.
Per offline discussion, this makes sense to do - we should correctly add this behaviour to make sure that we're reflecting what is the "correct" default
As we've said, I think adding a CompatibilityOption for this, so folks can revert back to the old behaviour would be best
But I'm of the opinion that moving folks over by default to the correct code path (which is technically breaking their generated code) is best
Do we know if we could use go:fix or anything to help them migrate more easily?
Member
Author
|
I don't know how to use go:fix to fix this. I think we'll document the change and mention it in the release. |
f55a1b0 to
843d5d6
Compare
843d5d6 to
241797e
Compare
Closes: oapi-codegen#2267 Response headers in strict server code were always generated as direct value types regardless of whether they were required. This adds support for optional and nullable response headers in the strict-interface and strict-responses templates. Add Required and Nullable fields to ResponseHeaderDefinition, populated from the OpenAPI header object. Add GoTypeDef(), IsOptional(), and IsNullable() methods that mirror the existing Property logic for determining whether a field should be a pointer, nullable.Nullable[T], or a direct value. Update strict-interface.tmpl and strict-responses.tmpl to use GoTypeDef for struct field types, and to conditionally guard w.Header().Set() calls with nil/IsSpecified checks for optional/nullable headers. Note: this is a breaking change for specs where response headers lack explicit `required: true`, since the OpenAPI default is false. Existing headers will change from direct values to pointers. This may need to be gated behind a config option. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Allows users to restore the pre-v2.5.0 behavior where all response headers are treated as required, ignoring the OpenAPI `required` field. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
241797e to
bf6c769
Compare
jamietanna
approved these changes
Apr 11, 2026
This was referenced Apr 30, 2026
mromaszewicz
added a commit
that referenced
this pull request
Apr 30, 2026
Bring the fiber and iris strict-server interface templates back into parity with the canonical stdhttp template (`strict-interface.tmpl`). Four pieces of drift were addressed: 1. Nullable / optional response-header serialization. Both templates now use the same three-way switch on `.IsNullable` / `.IsOptional` / default that PR #2301 introduced for stdhttp, so unspecified nullable values and nil optional pointers are skipped instead of being stringified into the wire header. 2. Response-header struct field types. The `{{$opid}}{{$statusCode}}ResponseHeaders` struct now uses `{{.GoTypeDef}}` (pointer/nullable-aware) rather than the raw `{{.Schema.TypeDecl}}`, matching the type that the typed-body Visit function expects. 3. `$ref` Text responses. The fixed-status-code + ref branch now matches Multipart and Text together (PR #2225), so `$ref` text responses alias directly to the component response type. Iris additionally drops its unconditional `type X string` short-circuit, which previously masked `$ref`, `$hasHeaders`, and `$fixedStatusCode` for any text response. 4. `$ref` name qualification (fiber only). Switch from `ucFirst` to `ucFirstWithPkgName` so external-ref response types carry their package qualifier, matching stdhttp and iris. Regenerated fixtures under `internal/test/strict-server/{fiber,iris}/server.gen.go` demonstrate the change for items (1) and (2); items (3) and (4) have no existing fiber/iris fixture coverage. The no-content `Visit*Response` branch still renders headers unconditionally in all three templates (including stdhttp); that gap is tracked separately (#2349) and not addressed here. Fixes: #2331 Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
mromaszewicz
added a commit
that referenced
this pull request
Apr 30, 2026
…2351) The no-content branch of the three strict-server interface templates (`strict-interface.tmpl`, `strict-fiber-interface.tmpl`, `strict-iris-interface.tmpl`) was rendering response headers unconditionally, missing the three-way `.IsNullable` / `.IsOptional` / default switch that PR #2301 added to the typed-body branch. Because the `{{$opid}}{{$statusCode}}ResponseHeaders` struct is shared between the typed-body and no-content branches and uses `{{.GoTypeDef}}` (since #2301 / #2331), an unset optional header field is `*string(nil)` and an unspecified nullable header is a zero `runtime.Nullable[T]`. The bare `fmt.Sprint(...)` in the no-content branch was therefore stringifying these to `<nil>` / a typed zero-value and emitting a junk header rather than omitting it. A 204-style response declaring an optional or nullable header — with the caller leaving it unset — produced a wrong header value instead of no header at all. Apply the same three-way switch to the no-content branch in all three templates so unset values are skipped, matching the typed-body branch behavior and matching what callers expect for OpenAPI `required: false` / `nullable: true` response headers. Add regression coverage: - `strict-schema.yaml` gains a `/no-content-headers` POST operation whose only response is 204 with `optional-header` (`required: false`) and `nullable-header` (`nullable: true`). - `NoContentHeaders` handler stub returning an empty `NoContentHeaders204Response{}` is wired into all seven framework server implementations (chi, echo, fiber, gin, gorilla, iris, stdhttp). - `NoContentHeadersOmitUnset` subtest added to the three `testImpl` copies (shared, stdhttp, fiber). Asserts both headers are absent from `rr.Header().Values(...)` when the response struct's header fields are left at their zero values. The shared `testImpl` covers chi, echo, gin, and iris in one place; stdhttp and fiber maintain their own copies because they live in separate test files (stdhttp because of its own go.mod, fiber because of its package boundary). Gorilla generates code but has no test driver — the handler stub is still required so the generated `StrictServerInterface` is satisfied. Fixes: #2349 Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
This was referenced May 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes: #2267
Response headers in strict server code were always generated as direct value types regardless of whether they were required. This adds support for optional and nullable response headers in the strict-interface and strict-responses templates.
Add Required and Nullable fields to ResponseHeaderDefinition, populated from the OpenAPI header object. Add GoTypeDef(), IsOptional(), and IsNullable() methods that mirror the existing Property logic for determining whether a field should be a pointer, nullable.Nullable[T], or a direct value.
Update strict-interface.tmpl and strict-responses.tmpl to use GoTypeDef for struct field types, and to conditionally guard w.Header().Set() calls with nil/IsSpecified checks for optional/nullable headers.
Note: this is a breaking change for specs where response headers lack explicit
required: true, since the OpenAPI default is false. Existing headers will change from direct values to pointers. This may need to be gated behind a config option.