Skip to content

feat!: axum 0.8, plus the extractors and router methods it was hiding - #73

Merged
Kilerd merged 1 commit into
mainfrom
feat/axum-0.8
Aug 2, 2026
Merged

feat!: axum 0.8, plus the extractors and router methods it was hiding#73
Kilerd merged 1 commit into
mainfrom
feat/axum-0.8

Conversation

@Kilerd

@Kilerd Kilerd commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Three things at once, because they belong in the same breaking release.

1. axum 0.7 → 0.8

The headline change is path syntax: a captured segment is written {id}, not :id, and axum refuses the old form at startup rather than matching it literally:

Path segments must not start with `:`. For capture groups, use `{capture}`.

That is a breaking change for every application's routes, which is exactly why it belongs here rather than in 0.5 — asking users to migrate their config in 0.4 and their routes in 0.5 is two disruptions where one will do.

It also simplifies gotcha: {id} is what OpenAPI already used, so the framework no longer translates between the two syntaxes. replace_path_variable becomes the identity (kept, documented, and tested as such), and the parameter-name extraction in ParameterProvider reads {name}.

Other adjustments the upgrade forced:

  • axum-core 0.5 dropped #[async_trait] from its extractor traits, so Valid, Header and Cookie use a plain async fn — reversing the attribute that E0195 demanded under 0.7.
  • Router::layer gained Sync bounds, mirrored on GotchaRouter::layer and the builder.
  • axum-extra 0.10, axum-macros 0.5, axum-prometheus 0.8; the two examples carrying their own axum dependency move too.
  • http = "0.2" dropped — unused, and two major versions behind what axum itself uses.

2. fallback_service

Unmatched requests can now go to a Service rather than a handler — serving an SPA's index.html with ServeFile, or forwarding to a proxy, where fallback would need a wrapper.

3. The extractors that were there but unreachable

Re-exported, so none of these need gotcha::axum::… any more:

note
Form, Multipart the features were already enabled and Multipart already had a ParameterProvider impl — they were simply never re-exported
Sse, Event, KeepAlive needed no new axum feature
WebSocketUpgrade, WebSocket turns on axum's ws feature
middleware for middleware::from_fn
MatchedPath, OriginalUri turns on matched-path / original-uri

The websocket frame type stays behind ws::Message, because Message is already the message-system trait — re-exporting it bare would have been an ambiguity.

Tests

tests/pass/handler/axum_surface.rs exercises all of it: a urlencoded body, a multipart upload, an SSE stream, a websocket upgrade that echoes a frame, MatchedPath/OriginalUri, middleware::from_fn, and fallback_service.

Migration

MIGRATION.md now leads the 0.4 section with the route-path change, since it is the edit every application must make — ahead of the configuration change it previously led with.

Verification

Workspace builds with --all-features, every feature combination tests, clippy --all-features --workspace, fmt.

🤖 Generated with Claude Code

**axum 0.7 -> 0.8.** The headline change is that a captured path segment is now written `{id}`
rather than `:id`, and axum rejects the old form at startup instead of matching it literally. That
is a breaking change for every application's routes, so it belongs in this release rather than
after it.

It also simplifies gotcha: `{id}` is what OpenAPI already used, so the framework no longer
translates between the two. `replace_path_variable` is the identity, kept as documentation of that
fact, and `path_variable_names` (plus the `ParameterProvider` regexes) now read `{name}`.

Other adjustments the upgrade forced:

- axum-core 0.5 dropped `#[async_trait]` from its extractor traits, so `Valid`, `Header` and
  `Cookie` use plain `async fn` — reversing the attribute that E0195 required under 0.7.
- `Router::layer` gained `Sync` bounds, mirrored on `GotchaRouter::layer` and the builder.
- axum-extra 0.10, axum-macros 0.5, axum-prometheus 0.8; the two examples with their own `axum`
  dependency move too, and `http = "0.2"` is dropped (it was unused, and two major versions behind).

**Capabilities axum had that gotcha did not surface**, now re-exported so they no longer need
`gotcha::axum::…`: `Form` and `Multipart` (whose features were already enabled and whose schema
support already existed), `Sse`/`Event`/`KeepAlive`, `WebSocketUpgrade`/`WebSocket`, `middleware`
for `from_fn`, and `MatchedPath`/`OriginalUri`. The `ws`, `matched-path` and `original-uri`
features are turned on for those. The websocket frame type stays behind `ws::Message`, since
`Message` is already the message-system trait.

**`GotchaRouter::fallback_service`** handles unmatched requests with a `Service` rather than a
handler — serving an SPA's index with `ServeFile`, say.

A pass test exercises all of it, and MIGRATION.md leads with the route-path change since it is the
edit every application has to make.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Kilerd
Kilerd merged commit 73c688f into main Aug 2, 2026
11 checks passed
@Kilerd Kilerd mentioned this pull request Aug 2, 2026
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.

1 participant