Skip to content

fix(openapi): unit-returning handlers compile and document an empty body (closes #62) - #69

Merged
Kilerd merged 1 commit into
mainfrom
fix/unit-return-handlers
Aug 1, 2026
Merged

fix(openapi): unit-returning handlers compile and document an empty body (closes #62)#69
Kilerd merged 1 commit into
mainfrom
fix/unit-return-handlers

Conversation

@Kilerd

@Kilerd Kilerd commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Closes #62. Two problems with handlers that return nothing.

1. #[api] on a handler with no return type didn't compile

#[api(id = "no_return")]
async fn no_return() {}     // error[E0782]: expected a type, found a trait

The ReturnType::Default branch generated ( () as ::gotcha::Responsible) — a cast to a trait. So an ordinary "returns nothing" handler (a DELETE, say) could not be annotated at all. The existing no-args-handler test only covers no arguments; its handler returns String, so this path was never exercised.

Now emits <() as Responsible>::response().

2. () documented as "type": "void" — not a valid OpenAPI type

{"200":{"content":{"application/json":{"schema":{"type":"void"}}}}}

void isn't among the permitted values, so the document was invalid and client generators reject it. A new Schematic::empty_body() (default false, true for ()) lets the blanket Responsible impl document the unit type as a response with no content.

Which status? 200, not 204

Worth stating explicitly, since 204 No Content is the intuitive guess. axum sends 200 with an empty body:

impl IntoResponse for () { Body::empty().into_response() }   // axum-core/src/response/into_response.rs:126
impl IntoResponse for Body { Response::new(self) }           // :167

http::Response::new defaults to StatusCode::OK, and there is no NO_CONTENT anywhere in axum-core's response module. Documenting 204 would have recreated exactly the doc-vs-reality mismatch this PR is fixing. A handler that wants 204 returns StatusCode::NO_CONTENT explicitly.

The test pins this down by asserting the documented status against ().into_response().status() itself, so if axum ever changes, the test fails rather than the document silently lying.

Json<()> is deliberately left alone

Wrapping the unit in Json is genuinely different: it really does send null with a 200. That keeps its JSON response; only the schema changed, from the invalid "void" to an empty schema.

Test

tests/pass/openapi/unit_responses.rs covers all three shapes — no return type, explicit -> (), and Json<()> — asserting the response carries no content and that "void" appears nowhere.

Verified locally: gotcha (openapi) + gotcha_core suites, cargo clippy --all-features --workspace, cargo fmt --check.

🤖 Generated with Claude Code

…ody (closes #62)

Two problems with handlers that return nothing.

`#[api]` on a handler with no return type did not compile at all: the `ReturnType::Default`
branch generated `( () as ::gotcha::Responsible)`, a cast to a *trait*, which is E0782. So an
ordinary "returns nothing" handler could not be annotated. The existing `no-args-handler` test
only covered no *arguments* — its handler returns `String` — so this path was never exercised.

`()` also documented as `{"type": "void"}`, which is not one of the permitted OpenAPI types, so
the generated document was invalid and client generators would reject it.

- The codegen now emits `<() as Responsible>::response()`.
- `Schematic::empty_body()` (default `false`, `true` for `()`) lets the blanket `Responsible` impl
  document the unit type as a response with **no content**.
- The documented status is `200`, matching what axum actually sends: `impl IntoResponse for ()`
  defers to `Body::empty()` and `http::Response::new` defaults to `StatusCode::OK`. It is not a
  `204` — a handler wanting that returns `StatusCode::NO_CONTENT` explicitly.
- `Json<()>` is deliberately unaffected: it really does send `null` with a 200, so it keeps its
  JSON response — but `()`'s schema is now an empty schema rather than the invalid `"void"`.

The pass test asserts the documented status against `().into_response().status()` itself, so the
document cannot drift away from the runtime behaviour.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Kilerd
Kilerd force-pushed the fix/unit-return-handlers branch from eba722d to 9627188 Compare August 1, 2026 12:45
@Kilerd Kilerd changed the title fix(openapi): unit-returning handlers compile and document 204 (closes #62) fix(openapi): unit-returning handlers compile and document an empty body (closes #62) Aug 1, 2026
@Kilerd
Kilerd merged commit ba88874 into main Aug 1, 2026
67 checks passed
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.

[openapi] Handlers returning nothing don't compile, and () documents as an invalid type: "void"

1 participant