PackageTrack
Sign in Get early access

axum

HTTP routing and request handling library that focuses on ergonomics and modularity

0.8.9 436M downloads/mo #264 most downloaded on crates.io tokio-rs/axum

What this package is like to depend on

Last release 4 months ago

14 Apr 2026

Release timing varies

gaps range from 2 weeks to 5 months

Nearly every release is documented

notes for 81 of 83 stable releases

5 versions withdrawn

withdrawn after publishing

5 years old

94 releases · first in 2021

5 releases in the last 12 months

see the full history below

Release timeline

94 releases · Jul 2021 to Apr 2026
2022 2023 2024 2025 2026
Release Pre-release Withdrawn

Releases

latest 60 of 94
  1. 0.8.9 14 Apr 2026
    Release notes
    • added: WebSocketUpgrade::{requested_protocols, set_selected_protocol} for more flexible subprotocol selection (#3597)
    • changed: Update minimum rust version to 1.80 (#3620)
    • fixed: Set connect endpoint on correct field in MethodRouter (#3656)
    • fixed: Return specific error message when multipart body limit is exceeded (#3611)
    Open source →
    Release notes
    • added: WebSocketUpgrade::{requested_protocols, set_selected_protocol} for more flexible subprotocol selection (#3597)
    • changed: Update minimum rust version to 1.80 (#3620)
    • fixed: Set connect endpoint on correct field in MethodRouter (#3656)
    • fixed: Return specific error message when multipart body limit is exceeded (#3611)
    Open source →
  2. 0.8.8 20 Dec 2025
    Release notes
    • Clarify documentation for Router::route_layer (#3567)
    Open source →
    Release notes
    • Clarify documentation for Router::route_layer (#3567)
    Open source →
  3. 0.8.7 14 Nov 2025
    Release notes
    • Relax implicit Send / Sync bounds on RouterAsService, RouterIntoService (#3555)
    • Make it easier to visually scan for default features (#3550)
    • Fix some documentation typos
    Open source →
    Release notes
    • Relax implicit Send / Sync bounds on RouterAsService, RouterIntoService (#3555)
    • Make it easier to visually scan for default features (#3550)
    • Fix some documentation typos
    Open source →
  4. 0.8.6 30 Sep 2025
    Release notes

    Released without changes to fix docs.rs build.

    Open source →
  5. 0.8.5 28 Sep 2025
    Release notes
    • fixed: Reject JSON request bodies with trailing characters after the JSON document (#3453)
    • added: Implement OptionalFromRequest for Multipart (#3220)
    • added: Getter methods Location::{status_code, location}
    • added: Support for writing arbitrary binary data into server-sent events (#3425)]
    • added: middleware::ResponseAxumBodyLayer for mapping response body to axum::body::Body (#3469)
    • added: impl FusedStream for WebSocket (#3443)
    • changed: The sse module and Sse type no longer depend on the tokio feature (#3154)
    • changed: If the location given to one of Redirects constructors is not a valid header value, instead of panicking on construction, the IntoResponse impl now returns an HTTP 500, just like Json does when serialization fails (#3377)
    • changed: Update minimum rust version to 1.78 (#3412)
    Open source →
  6. 0.8.4 30 Apr 2025
    Release notes
    • added: Router::reset_fallback (#3320)
    • added: WebSocketUpgrade::selected_protocol (#3248)
    • fixed: Panic location for overlapping method routes (#3319)
    • fixed: Don't leak a tokio task when using serve without graceful shutdown (#3129)
    Open source →
  7. 0.8.3 28 Mar 2025
    Release notes
    • added: Implement From<Bytes> for Message (#3273)
    • added: Implement OptionalFromRequest for Json (#3142)
    • added: Implement OptionalFromRequest for Extension (#3157)
    • added: Allow setting the read buffer capacity of WebSocketUpgrade (#3178)
    • changed: Improved code size / compile time of dependent crates (#3285, #3294)
    Open source →
  8. 0.8.2 21 Jan 2025 withdrawn
    Release notes

    Yanked from crates.io due to unforeseen breaking change, see #3190 for details.

    Open source →
  9. 0.8.1 01 Jan 2025
    Release notes
    • fixed: Removed the warning about breaking changes from README
    Open source →
  10. 0.8.0 01 Jan 2025
    Release notes

    since rc.1

    <details>

    • breaking: axum::extract::ws::Message now uses Bytes in place of Vec<u8>, and a new Utf8Bytes type in place of String, for its variants (#3078)
    • breaking: Remove OptionalFromRequestParts impl for Query (#3088)
    • changed: Upgraded tokio-tungstenite to 0.26 (#3078)
    • changed: Query/Form: Use serde_path_to_error to report fields that failed to parse (#3081)

    </details>

    full changelog

    Note: there are further relevant changes in axum-core's changelog

    • breaking: Upgrade matchit to 0.8, changing the path parameter syntax from /:single and /*many to /{single} and /{*many}; the old syntax produces a panic to avoid silent change in behavior (#2645)
    • breaking: Require Sync for all handlers and services added to Router and MethodRouter (#2473)
    • breaking: The tuple and tuple_struct Path extractor deserializers now check that the number of parameters matches the tuple length exactly (#2931)
    • breaking: Move Host extractor to axum-extra (#2956)
    • breaking: Remove WebSocket::close. Users should explicitly send close messages themselves. (#2974)
    • breaking: Make serve generic over the listener and IO types (#2941)
    • breaking: Remove Serve::tcp_nodelay and WithGracefulShutdown::tcp_nodelay. See serve::ListenerExt for an API that let you set arbitrary TCP stream properties. (#2941)
    • breaking: Option<Path<T>> no longer swallows all error conditions, instead rejecting the request in many cases; see its documentation for details (#2475)
    • breaking: axum::extract::ws::Message now uses Bytes in place of Vec<u8>, and a new Utf8Bytes type in place of String, for its variants (#3078)
    • fixed: Skip SSE incompatible chars of serde_json::RawValue in Event::json_data (#2992)
    • fixed: Don't panic when array type is used for path segment (#3039)
    • fixed: Avoid setting content-length before middleware. This allows middleware to add bodies to requests without needing to manually set content-length (#2897)
    • change: Update minimum rust version to 1.75 (#2943)
    • changed: Upgraded tokio-tungstenite to 0.26 (#3078)
    • changed: Query/Form: Use serde_path_to_error to report fields that failed to parse (#3081)
    • added: Add method_not_allowed_fallback to set a fallback when a path matches but there is no handler for the given HTTP method (#2903)
    • added: Add NoContent as a self-described shortcut for StatusCode::NO_CONTENT (#2978)
    • added: Add support for WebSockets over HTTP/2. They can be enabled by changing get(ws_endpoint) handlers to any(ws_endpoint) (#2894)
    • added: Add MethodFilter::CONNECT, routing::connect[_service] and MethodRouter::connect[_service] (#2961)
    • added: Extend FailedToDeserializePathParams::kind enum with (ErrorKind::DeserializeError) This new variant captures both key, value, and message from named path parameters parse errors, instead of only deserialization error message in ErrorKind::Message. (#2720)

    rc.1

    Note: there are further relevant changes in axum-core's changelog

    • breaking: Move Host extractor to axum-extra (#2956)
    • breaking: Remove WebSocket::close. Users should explicitly send close messages themselves. (#2974)
    • breaking: Make serve generic over the listener and IO types (#2941)
    • breaking: Remove Serve::tcp_nodelay and WithGracefulShutdown::tcp_nodelay. See serve::ListenerExt for an API that let you set arbitrary TCP stream properties. (#2941)
    • breaking: Option<Path<T>> and Option<Query<T>> no longer swallow all error conditions, instead rejecting the request in many cases; see their documentation for details (#2475)
    • fixed: Skip SSE incompatible chars of serde_json::RawValue in Event::json_data (#2992)
    • fixed: Don't panic when array type is used for path segment (#3039)
    • fixed: Avoid setting content-length before middleware. This allows middleware to add bodies to requests without needing to manually set content-length (#2897)
    • added: Add method_not_allowed_fallback to set a fallback when a path matches but there is no handler for the given HTTP method (#2903)
    • added: Add NoContent as a self-described shortcut for StatusCode::NO_CONTENT (#2978)
    • added: Add support for WebSockets over HTTP/2. They can be enabled by changing get(ws_endpoint) handlers to any(ws_endpoint) (#2894)
    • added: Add MethodFilter::CONNECT, routing::connect[_service] and MethodRouter::connect[_service] (#2961)
    • added: Extend FailedToDeserializePathParams::kind enum with (ErrorKind::DeserializeError) This new variant captures both key, value, and message from named path parameters parse errors, instead of only deserialization error message in ErrorKind::Message. (#2720)

    alpha.1

    • breaking: Require Sync for all handlers and services added to Router and MethodRouter (#2473)
    • breaking: The tuple and tuple_struct Path extractor deserializers now check that the number of parameters matches the tuple length exactly (#2931)
    • breaking: Upgrade matchit to 0.8, changing the path parameter syntax from /:single and /*many to /{single} and /{*many}; the old syntax produces a panic to avoid silent change in behavior (#2645)
    • change: Update minimum rust version to 1.75 (#2943)
    Open source →
  11. 0.8.0-rc.1 17 Dec 2024 pre-release

    Nothing published for this version

  12. 0.8.0-alpha.1 05 Oct 2024 pre-release

    Nothing published for this version

  13. 0.7.9 16 Nov 2024
    Release notes
    • fixed: Avoid setting content-length before middleware (#3031)
    Open source →
  14. 0.7.8 15 Nov 2024
    Release notes
    • fixed: Skip SSE incompatible chars of serde_json::RawValue in Event::json_data (#2992)
    • added: Add method_not_allowed_fallback to set a fallback when a path matches but there is no handler for the given HTTP method (#2903)
    • added: Add MethodFilter::CONNECT, routing::connect[_service] and MethodRouter::connect[_service] (#2961)
    • added: Add NoContent as a self-described shortcut for StatusCode::NO_CONTENT (#2978)
    Open source →
  15. 0.7.7 27 Sep 2024
    Release notes
    • change: Remove manual tables of content from the documentation, since rustdoc now generates tables of content in the sidebar (#2921)
    Open source →
  16. 0.7.6 20 Sep 2024
    Release notes
    • change: Avoid cloning Arc during deserialization of Path
    • added: axum::serve::Serve::tcp_nodelay and axum::serve::WithGracefulShutdown::tcp_nodelay (#2653)
    • added: Router::has_routes function (#2790)
    • change: Update tokio-tungstenite to 0.23 (#2841)
    • added: Serve::local_addr and WithGracefulShutdown::local_addr functions (#2881)
    Open source →
  17. 0.7.5 24 Mar 2024
    Release notes
    • fixed: Fixed layers being cloned when calling axum::serve directly with a Router or MethodRouter (#2586)
    • fixed: h2 is no longer pulled as a dependency unless the http2 feature is enabled (#2605)
    • added: Add #[debug_middleware] (#1993, #2725)
    Open source →
  18. 0.7.4 13 Jan 2024
    Release notes
    • fixed: Fix performance regression present since axum 0.7.0 (#2483)
    • fixed: Improve debug_handler on tuple response types (#2201)
    • added: Add must_use attribute to Serve and WithGracefulShutdown (#2484)
    • added: Re-export axum_core::body::BodyDataStream from axum
    Open source →
  19. 0.7.3 29 Dec 2023
    Release notes
    • added: Body implements From<()> now (#2411)
    • change: Update version of multer used internally for multipart (#2433)
    • change: Update tokio-tungstenite to 0.21 (#2435)
    • added: Enable tracing feature by default (#2460)
    • added: Support graceful shutdown on serve (#2398)
    • added: RouterIntoService implements Clone (#2456)
    Open source →
  20. 0.7.2 04 Dec 2023
    Release notes
    • added: Add axum::body::to_bytes (#2373)
    • fixed: Gracefully handle accept errors in serve (#2400)
    Open source →
  21. 0.7.1 27 Nov 2023
    Release notes
    • fix: Fix readme.
    Open source →
  22. 0.7.0 27 Nov 2023
    Release notes
    • breaking: Update public dependencies. axum now requires
    • breaking: axum now requires tower-http 0.5
    • breaking: Remove deprecated WebSocketUpgrade::max_send_queue
    • breaking: The following types/traits are no longer generic over the request body (i.e. the B type param has been removed) (#1751 and #1789):
      • FromRequestParts
      • FromRequest
      • HandlerService
      • HandlerWithoutStateExt
      • Handler
      • LayeredFuture
      • Layered
      • MethodRouter
      • Next
      • RequestExt
      • RouteFuture
      • Route
      • Router
    • breaking: axum no longer re-exports hyper::Body as that type is removed in hyper 1.0. Instead axum has its own body type at axum::body::Body (#1751)
    • breaking: extract::BodyStream has been removed as body::Body implements Stream and FromRequest directly (#1751)
    • breaking: Change sse::Event::json_data to use axum_core::Error as its error type (#1762)
    • breaking: Rename DefaultOnFailedUpdgrade to DefaultOnFailedUpgrade (#1664)
    • breaking: Rename OnFailedUpdgrade to OnFailedUpgrade (#1664)
    • breaking: TypedHeader has been moved to axum-extra as axum_extra::TypedHeader and requires enabling the typed-header feature on axum-extra. The headers feature has been removed from axum; what it provided under axum::headers is now found in axum_extra::headers by default. (#1850)
    • breaking: Removed re-exports of Empty and Full. Use axum::body::Body::empty and axum::body::Body::from respectively (#1789)
    • breaking: The response returned by IntoResponse::into_response must use axum::body::Body as the body type. axum::response::Response does this (#1789)
    • breaking: Removed the BoxBody type alias and its box_body constructor. Use axum::body::Body::new instead (#1789)
    • breaking: Remove RawBody extractor. axum::body::Body implements FromRequest directly (#1789)
    • breaking: The following types from http-body no longer implement IntoResponse:
      • Full, use Body::from instead
      • Empty, use Body::empty instead
      • BoxBody, use Body::new instead
      • UnsyncBoxBody, use Body::new instead
      • MapData, use Body::new instead
      • MapErr, use Body::new instead
    • added: Add axum::extract::Request type alias where the body is axum::body::Body (#1789)
    • added: Add Router::as_service and Router::into_service to workaround type inference issues when calling ServiceExt methods on a Router (#1835)
    • breaking: Removed axum::Server as it was removed in hyper 1.0. Instead use axum::serve(listener, service) or hyper/hyper-util for more configuration options (#1868)
    • breaking: Only inherit fallbacks for routers nested with Router::nest. Routers nested with Router::nest_service will no longer inherit fallbacks (#1956)
    • fixed: Don't remove the Sec-WebSocket-Key header in WebSocketUpgrade (#1972)
    • added: Add axum::extract::Query::try_from_uri (#2058)
    • added: Implement IntoResponse for Box<str> and Box<[u8]> ([#2035])
    • breaking: Simplify MethodFilter. It no longer uses bitflags (#2073)
    • fixed: Fix bugs around merging routers with nested fallbacks (#2096)
    • fixed: Fix .source() of composite rejections (#2030)
    • fixed: Allow unreachable code in #[debug_handler] (#2014)
    • change: axum's MSRV is now 1.66 (#1882)
    • added: Implement IntoResponse for (R,) where R: IntoResponse (#2143)
    • changed: For SSE, add space between field and value for compatibility (#2149)
    • added: Add NestedPath extractor (#1924)
    • added: Add handle_error function to existing ServiceExt trait (#2235)
    • breaking: impl<T> IntoResponse(Parts) for Extension<T> now requires T: Clone, as that is required by the http crate (#1882)
    • added: Add axum::Json::from_bytes (#2244)
    • added: Implement FromRequestParts for http::request::Parts (#2328)
    • added: Implement FromRequestParts for http::Extensions (#2328)
    • fixed: Clearly document applying DefaultBodyLimit to individual routes (#2157)
    Open source →
  23. 0.6.20 03 Aug 2023
    Release notes
    • added: WebSocketUpgrade::write_buffer_size and WebSocketUpgrade::max_write_buffer_size
    • changed: Deprecate WebSocketUpgrade::max_send_queue
    • change: Update tokio-tungstenite to 0.20
    • added: Implement Handler for T: IntoResponse (#2140)
    Open source →
  24. 0.6.19 17 Jul 2023
    Release notes
    • added: Add axum::extract::Query::try_from_uri (#2058)
    • added: Implement IntoResponse for Box<str> and Box<[u8]> (#2035)
    • fixed: Fix bugs around merging routers with nested fallbacks (#2096)
    • fixed: Fix .source() of composite rejections (#2030)
    • fixed: Allow unreachable code in #[debug_handler] (#2014)
    • change: Update tokio-tungstenite to 0.19 (#2021)
    • change: axum's MSRV is now 1.63 (#2021)
    Open source →
  25. 0.6.18 30 Apr 2023
    Release notes
    • fixed: Don't remove the Sec-WebSocket-Key header in WebSocketUpgrade (#1972)
    Open source →
  26. 0.6.17 25 Apr 2023
    Release notes
    • fixed: Fix fallbacks causing a panic on CONNECT requests (#1958)
    Open source →
  27. 0.6.16 18 Apr 2023
    Release notes
    • fixed: Don't allow extracting MatchedPath in fallbacks (#1934)
    • fixed: Fix panic if Router with something nested at / was used as a fallback (#1934)
    • added: Document that Router::new().fallback(...) isn't optimal (#1940)
    Open source →
  28. 0.6.15 12 Apr 2023
    Release notes
    • fixed: Removed additional leftover debug messages (#1927)
    Open source →
  29. 0.6.14 11 Apr 2023
    Release notes
    • fixed: Removed leftover "path_router hit" debug message (#1925)
    Open source →
  30. 0.6.13 11 Apr 2023
    Release notes
    • added: Log rejections from built-in extractors with the axum::rejection=trace target (#1890)
    • fixed: Fixed performance regression with Router::nest introduced in 0.6.0. nest now flattens the routes which performs better (#1711)
    • fixed: Extracting MatchedPath in nested handlers now gives the full matched path, including the nested path (#1711)
    • added: Implement Deref and DerefMut for built-in extractors (#1922)
    Open source →
  31. 0.6.12 22 Mar 2023
    Release notes
    • added: Implement IntoResponse for MultipartError (#1861)
    • fixed: More clearly document what wildcards matches (#1873)
    Open source →
  32. 0.6.11 13 Mar 2023
    Release notes
    • fixed: Don't require S: Debug for impl Debug for Router<S> (#1836)
    • fixed: Clone state a bit less when handling requests (#1837)
    • fixed: Unpin itoa dependency (#1815)
    Open source →
  33. 0.6.10 03 Mar 2023
    Release notes
    • fixed: Add #[must_use] attributes to types that do nothing unless used (#1809)
    • fixed: Gracefully handle missing headers in the TypedHeader extractor (#1810)
    • fixed: Fix routing issues when loading a Router via a dynamic library (#1806)
    Open source →
  34. 0.6.9 27 Feb 2023
    Release notes
    • changed: Update to tower-http 0.4. axum is still compatible with tower-http 0.3 (#1783)
    Open source →
  35. 0.6.8 24 Feb 2023
    Release notes
    • fixed: Fix Allow missing from routers with middleware (#1773)
    • added: Add KeepAlive::event for customizing the event sent for SSE keep alive (#1729)
    Open source →
  36. 0.6.7 17 Feb 2023
    Release notes
    • added: Add FormRejection::FailedToDeserializeFormBody which is returned if the request body couldn't be deserialized into the target type, as opposed to FailedToDeserializeForm which is only for query parameters (#1683)
    • added: Add MockConnectInfo for setting ConnectInfo during tests (#1767)
    Open source →
  37. 0.6.6 12 Feb 2023
    Release notes
    • fixed: Enable passing MethodRouter to Router::fallback (#1730)
    Open source →
  38. 0.6.5 11 Feb 2023
    Release notes
    • fixed: Fix #[debug_handler] sometimes giving wrong borrow related suggestions (#1710)
    • Document gotchas related to using impl IntoResponse as the return type from handler functions (#1736)
    Open source →
  39. 0.6.4 24 Jan 2023
    Release notes
    • Depend on axum-macros 0.3.2
    Open source →
  40. 0.6.3 20 Jan 2023
    Release notes
    • added: Implement IntoResponse for &'static [u8; N] and [u8; N] (#1690)
    • fixed: Make Path support types using serde::Deserializer::deserialize_any (#1693)
    • added: Add RawPathParams (#1713)
    • added: Implement Clone and Service for axum::middleware::Next (#1712)
    • fixed: Document required tokio features to run "Hello, World!" example (#1715)
    Open source →
  41. 0.6.2 09 Jan 2023
    Release notes
    • added: Add body_text and status methods to built-in rejections (#1612)
    • added: Enable the runtime feature of hyper when using tokio (#1671)
    Open source →
  42. 0.6.1 29 Nov 2022
    Release notes
    • added: Expand the docs for Router::with_state (#1580)
    Open source →
  43. 0.6.0 25 Nov 2022
    Release notes

    Routing

    • fixed: Nested routers are now allowed to have fallbacks (#1521):

      let api_router = Router::new()
          .route("/users", get(|| { ... }))
          .fallback(api_fallback);
      
      let app = Router::new()
          // this would panic in 0.5 but in 0.6 it just works
          //
          // requests starting with `/api` but not handled by `api_router`
          // will go to `api_fallback`
          .nest("/api", api_router);
      

      The outer router's fallback will still apply if a nested router doesn't have its own fallback:

      // this time without a fallback
      let api_router = Router::new().route("/users", get(|| { ... }));
      
      let app = Router::new()
          .nest("/api", api_router)
          // `api_router` will inherit this fallback
          .fallback(app_fallback);
      
    • breaking: The request /foo/ no longer matches /foo/*rest. If you want to match /foo/ you have to add a route specifically for that (#1086)

      For example:

      use axum::{Router, routing::get, extract::Path};
      
      let app = Router::new()
          // this will match `/foo/bar/baz`
          .route("/foo/*rest", get(handler))
          // this will match `/foo/`
          .route("/foo/", get(handler))
          // if you want `/foo` to match you must also add an explicit route for it
          .route("/foo", get(handler));
      
      async fn handler(
          // use an `Option` because `/foo/` and `/foo` don't have any path params
          params: Option<Path<String>>,
      ) {}
      
    • breaking: Path params for wildcard routes no longer include the prefix /. e.g. /foo.js will match /*filepath with a value of foo.js, not /foo.js (#1086)

      For example:

      use axum::{Router, routing::get, extract::Path};
      
      let app = Router::new().route("/foo/*rest", get(handler));
      
      async fn handler(
          Path(params): Path<String>,
      ) {
          // for the request `/foo/bar/baz` the value of `params` will be `bar/baz`
          //
          // on 0.5 it would be `/bar/baz`
      }
      
    • fixed: Routes like /foo and /*rest are no longer considered overlapping. /foo will take priority (#1086)

      For example:

      use axum::{Router, routing::get};
      
      let app = Router::new()
          // this used to not be allowed but now just works
          .route("/foo/*rest", get(foo))
          .route("/foo/bar", get(bar));
      
      async fn foo() {}
      
      async fn bar() {}
      
    • breaking: Automatic trailing slash redirects have been removed. Previously if you added a route for /foo, axum would redirect calls to /foo/ to /foo (or vice versa for /foo/):

      use axum::{Router, routing::get};
      
      let app = Router::new()
          // a request to `GET /foo/` will now get `404 Not Found`
          // whereas in 0.5 axum would redirect to `/foo`
          //
          // same goes the other way if you had the route `/foo/`
          // axum will no longer redirect from `/foo` to `/foo/`
          .route("/foo", get(handler));
      
      async fn handler() {}
      

      Either explicitly add routes for /foo and /foo/ or use axum_extra::routing::RouterExt::route_with_tsr if you want the old behavior (#1119)

    • breaking: Router::fallback now only accepts Handlers (similarly to what get, post, etc. accept). Use the new Router::fallback_service for setting any Service as the fallback (#1155)

      This fallback on 0.5:

      use axum::{Router, handler::Handler};
      
      let app = Router::new().fallback(fallback.into_service());
      
      async fn fallback() {}
      

      Becomes this in 0.6

      use axum::Router;
      
      let app = Router::new().fallback(fallback);
      
      async fn fallback() {}
      
    • breaking: It is no longer supported to nest twice at the same path, i.e. .nest("/foo", a).nest("/foo", b) will panic. Instead use .nest("/foo", a.merge(b))

    • breaking: It is no longer supported to nest a router and add a route at the same path, such as .nest("/a", _).route("/a", _). Instead use .nest("/a/", _).route("/a", _).

    • changed: Router::nest now only accepts Routers, the general-purpose Service nesting method has been renamed to nest_service (#1368)

    • breaking: Allow Error: Into<Infallible> for Route::{layer, route_layer} (#924)

    • breaking: MethodRouter now panics on overlapping routes (#1102)

    • breaking: Router::route now only accepts MethodRouters created with get, post, etc. Use the new Router::route_service for routing to any Services (#1155)

    • breaking: Adding a .route_layer onto a Router or MethodRouter without any routes will now result in a panic. Previously, this just did nothing. #1327

    • breaking: RouterService has been removed since Router now implements Service when the state is (). Use Router::with_state to provide the state and get a Router<()>. Note that RouterService only existed in the pre-releases, not 0.5 (#1552)

    Extractors

    • added: Added new type safe State extractor. This can be used with Router::with_state and gives compile errors for missing states, whereas Extension would result in runtime errors (#1155)

      We recommend migrating from Extension to State for sharing application state since that is more type safe and faster. That is done by using Router::with_state and State.

      This setup in 0.5

      use axum::{routing::get, Extension, Router};
      
      let app = Router::new()
          .route("/", get(handler))
          .layer(Extension(AppState {}));
      
      async fn handler(Extension(app_state): Extension<AppState>) {}
      
      #[derive(Clone)]
      struct AppState {}
      

      Becomes this in 0.6 using State:

      use axum::{routing::get, extract::State, Router};
      
      let app = Router::new()
          .route("/", get(handler))
          .with_state(AppState {});
      
      async fn handler(State(app_state): State<AppState>) {}
      
      #[derive(Clone)]
      struct AppState {}
      

      If you have multiple extensions, you can use fields on AppState and implement FromRef:

      use axum::{extract::{State, FromRef}, routing::get, Router};
      
      let state = AppState {
          client: HttpClient {},
          database: Database {},
      };
      
      let app = Router::new().route("/", get(handler)).with_state(state);
      
      async fn handler(
          State(client): State<HttpClient>,
          State(database): State<Database>,
      ) {}
      
      // the derive requires enabling the "macros" feature
      #[derive(Clone, FromRef)]
      struct AppState {
          client: HttpClient,
          database: Database,
      }
      
      #[derive(Clone)]
      struct HttpClient {}
      
      #[derive(Clone)]
      struct Database {}
      
    • breaking: It is now only possible for one extractor per handler to consume the request body. In 0.5 doing so would result in runtime errors but in 0.6 it is a compile error (#1272)

      axum enforces this by only allowing the last extractor to consume the request.

      For example:

      use axum::{Json, http::HeaderMap};
      
      // This won't compile on 0.6 because both `Json` and `String` need to consume
      // the request body. You can use either `Json` or `String`, but not both.
      async fn handler_1(
          json: Json<serde_json::Value>,
          string: String,
      ) {}
      
      // This won't work either since `Json` is not the last extractor.
      async fn handler_2(
          json: Json<serde_json::Value>,
          headers: HeaderMap,
      ) {}
      
      // This works!
      async fn handler_3(
          headers: HeaderMap,
          json: Json<serde_json::Value>,
      ) {}
      

      This is done by reworking the FromRequest trait and introducing a new FromRequestParts trait.

      If your extractor needs to consume the request body then you should implement FromRequest, otherwise implement FromRequestParts.

      This extractor in 0.5:

      struct MyExtractor { /* ... */ }
      
      impl<B> FromRequest<B> for MyExtractor
      where
          B: Send,
      {
          type Rejection = StatusCode;
      
          async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
              // ...
          }
      }
      

      Becomes this in 0.6:

      use axum::{
          extract::{FromRequest, FromRequestParts},
          http::{StatusCode, Request, request::Parts},
      };
      
      struct MyExtractor { /* ... */ }
      
      // implement `FromRequestParts` if you don't need to consume the request body
      impl<S> FromRequestParts<S> for MyExtractor
      where
          S: Send + Sync,
      {
          type Rejection = StatusCode;
      
          async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
              // ...
          }
      }
      
      // implement `FromRequest` if you do need to consume the request body
      impl<S, B> FromRequest<S, B> for MyExtractor
      where
          S: Send + Sync,
          B: Send + 'static,
      {
          type Rejection = StatusCode;
      
          async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
              // ...
          }
      }
      

      For an example of how to write an extractor that accepts different Content-Types see the parse-body-based-on-content-type example.

    • added: FromRequest and FromRequestParts derive macro re-exports from axum-macros behind the macros feature (#1352)

    • added: Add RequestExt and RequestPartsExt which adds convenience methods for running extractors to http::Request and http::request::Parts (#1301)

    • added: JsonRejection now displays the path at which a deserialization error occurred (#1371)

    • added: Add extract::RawForm for accessing raw urlencoded query bytes or request body (#1487)

    • fixed: Used 400 Bad Request for FailedToDeserializeQueryString rejections, instead of 422 Unprocessable Entity (#1387)

    • changed: The inner error of a JsonRejection is now serde_path_to_error::Error<serde_json::Error>. Previously it was serde_json::Error (#1371)

    • changed: The default body limit now applies to the Multipart extractor (#1420)

    • breaking: ContentLengthLimit has been removed. Use DefaultBodyLimit instead (#1400)

    • breaking: RequestParts has been removed as part of the FromRequest rework (#1272)

    • breaking: BodyAlreadyExtracted has been removed (#1272)

    • breaking: The following types or traits have a new S type param which represents the state (#1155):

      • Router, defaults to ()
      • MethodRouter, defaults to ()
      • FromRequest, no default
      • Handler, no default
    • breaking: MatchedPath can now no longer be extracted in middleware for nested routes. In previous versions it returned invalid data when extracted from a middleware applied to a nested router. MatchedPath can still be extracted from handlers and middleware that aren't on nested routers (#1462)

    • breaking: Rename FormRejection::FailedToDeserializeQueryString to FormRejection::FailedToDeserializeForm (#1496)

    Middleware

    • added: Support running extractors on middleware::from_fn functions (#1088)
    • added: Add middleware::from_fn_with_state to enable running extractors that require state (#1342)
    • added: Add middleware::from_extractor_with_state (#1396)
    • added: Add map_request, map_request_with_state for transforming the request with an async function (#1408)
    • added: Add map_response, map_response_with_state for transforming the response with an async function (#1414)
    • added: Support any middleware response that implements IntoResponse (#1152)
    • breaking: Remove extractor_middleware which was previously deprecated. Use axum::middleware::from_extractor instead (#1077)
    • breaking: Require middleware added with Handler::layer to have Infallible as the error type (#1152)

    Misc

    • added: Support compiling to WASM. See the simple-router-wasm example for more details (#1382)
    • added: Add ServiceExt with methods for turning any Service into a MakeService similarly to Router::into_make_service (#1302)
    • added: String and binary From impls have been added to extract::ws::Message to be more inline with tungstenite (#1421)
    • added: Add #[derive(axum::extract::FromRef)] (#1430)
    • added: Add accept_unmasked_frames setting in WebSocketUpgrade (#1529)
    • added: Add WebSocketUpgrade::on_failed_upgrade to customize what to do when upgrading a connection fails (#1539)
    • fixed: Annotate panicking functions with #[track_caller] so the error message points to where the user added the invalid route, rather than somewhere internally in axum (#1248)
    • changed: axum's MSRV is now 1.60 (#1239)
    • changed: For methods that accept some S: Service, the bounds have been relaxed so the response type must implement IntoResponse rather than being a literal Response
    • breaking: New tokio default feature needed for WASM support. If you don't need WASM support but have default_features = false for other reasons you likely need to re-enable the tokio feature (#1382)
    • breaking: handler::{WithState, IntoService} are merged into one type, named HandlerService (#1418)

    <details> <summary>0.6.0 Pre-Releases</summary>

    Open source →
  44. 0.6.0-rc.5 18 Nov 2022 pre-release
    Release notes
    • breaking: Router::with_state is no longer a constructor. It is instead used to convert the router into a RouterService (#1532)

      This nested router on 0.6.0-rc.4

      Router::with_state(state).route(...);
      

      Becomes this in 0.6.0-rc.5

      Router::new().route(...).with_state(state);
      
    • breaking: Router::inherit_state has been removed. Use Router::with_state instead (#1532)

    • breaking: Router::nest and Router::merge now only supports nesting routers that use the same state type as the router they're being merged into. Use FromRef for substates (#1532)

    • added: Add accept_unmasked_frames setting in WebSocketUpgrade (#1529)

    • fixed: Nested routers will now inherit fallbacks from outer routers (#1521)

    • added: Add WebSocketUpgrade::on_failed_upgrade to customize what to do when upgrading a connection fails (#1539)

    Open source →
  45. 0.6.0-rc.4 09 Nov 2022 pre-release
    Release notes
    • changed: The inner error of a JsonRejection is now serde_path_to_error::Error<serde_json::Error>. Previously it was serde_json::Error (#1371)
    • added: JsonRejection now displays the path at which a deserialization error occurred (#1371)
    • fixed: Support streaming/chunked requests in ContentLengthLimit (#1389)
    • fixed: Used 400 Bad Request for FailedToDeserializeQueryString rejections, instead of 422 Unprocessable Entity (#1387)
    • added: Add middleware::from_extractor_with_state (#1396)
    • added: Add DefaultBodyLimit::max for changing the default body limit (#1397)
    • added: Add map_request, map_request_with_state for transforming the request with an async function (#1408)
    • added: Add map_response, map_response_with_state for transforming the response with an async function (#1414)
    • breaking: ContentLengthLimit has been removed. Use DefaultBodyLimit instead (#1400)
    • changed: Router no longer implements Service, call .into_service() on it to obtain a RouterService that does (#1368)
    • added: Add Router::inherit_state, which creates a Router with an arbitrary state type without actually supplying the state; such a Router can't be turned into a service directly (.into_service() will panic), but can be nested or merged into a Router with the same state type (#1368)
    • changed: Router::nest now only accepts Routers, the general-purpose Service nesting method has been renamed to nest_service (#1368)
    • added: Support compiling to WASM. See the simple-router-wasm example for more details (#1382)
    • breaking: New tokio default feature needed for WASM support. If you don't need WASM support but have default_features = false for other reasons you likely need to re-enable the tokio feature (#1382)
    • breaking: handler::{WithState, IntoService} are merged into one type, named HandlerService (#1418)
    • changed: The default body limit now applies to the Multipart extractor (#1420)
    • added: String and binary From impls have been added to extract::ws::Message to be more inline with tungstenite (#1421)
    • added: Add #[derive(axum::extract::FromRef)] (#1430)
    • added: FromRequest and FromRequestParts derive macro re-exports from [axum-macros] behind the macros feature (#1352)
    • breaking: MatchedPath can now no longer be extracted in middleware for nested routes (#1462)
    • added: Add extract::RawForm for accessing raw urlencoded query bytes or request body (#1487)
    • breaking: Rename FormRejection::FailedToDeserializeQueryString to FormRejection::FailedToDeserializeForm (#1496)
    Open source →
  46. 0.6.0-rc.3 09 Nov 2022 pre-release withdrawn
    Release notes

    Yanked, as it didn't compile in release mode.

    Open source →
  47. 0.6.0-rc.2 11 Sep 2022 pre-release
    Release notes

    Security

    • breaking: Added default limit to how much data Bytes::from_request will consume. Previously it would attempt to consume the entire request body without checking its length. This meant if a malicious peer sent an large (or infinite) request body your server might run out of memory and crash.

      The default limit is at 2 MB and can be disabled by adding the new DefaultBodyLimit::disable() middleware. See its documentation for more details.

      This also applies to these extractors which used Bytes::from_request internally:

      • Form
      • Json
      • String

      (#1346)

    Routing

    • breaking: Adding a .route_layer onto a Router or MethodRouter without any routes will now result in a panic. Previously, this just did nothing. #1327

    Middleware

    • added: Add middleware::from_fn_with_state and middleware::from_fn_with_state_arc to enable running extractors that require state (#1342)
    Open source →
  48. 0.6.0-rc.1 23 Aug 2022 pre-release
    Release notes

    Routing

    • breaking: Nested Routers will no longer delegate to the outer Router's fallback. Instead you must explicitly set a fallback on the inner Router (#1086)

      This nested router on 0.5:

      use axum::{Router, handler::Handler};
      
      let api_routes = Router::new();
      
      let app = Router::new()
          .nest("/api", api_routes)
          .fallback(fallback.into_service());
      
      async fn fallback() {}
      

      Becomes this in 0.6:

      use axum::Router;
      
      let api_routes = Router::new()
          // we have to explicitly set the fallback here
          // since nested routers no longer delegate to the outer
          // router's fallback
          .fallback(fallback);
      
      let app = Router::new()
          .nest("/api", api_routes)
          .fallback(fallback);
      
      async fn fallback() {}
      
    • breaking: The request /foo/ no longer matches /foo/*rest. If you want to match /foo/ you have to add a route specifically for that (#1086)

      For example:

      use axum::{Router, routing::get, extract::Path};
      
      let app = Router::new()
          // this will match `/foo/bar/baz`
          .route("/foo/*rest", get(handler))
          // this will match `/foo/`
          .route("/foo/", get(handler))
          // if you want `/foo` to match you must also add an explicit route for it
          .route("/foo", get(handler));
      
      async fn handler(
          // use an `Option` because `/foo/` and `/foo` don't have any path params
          params: Option<Path<String>>,
      ) {}
      
    • breaking: Path params for wildcard routes no longer include the prefix /. e.g. /foo.js will match /*filepath with a value of foo.js, not /foo.js (#1086)

      For example:

      use axum::{Router, routing::get, extract::Path};
      
      let app = Router::new().route("/foo/*rest", get(handler));
      
      async fn handler(
          Path(params): Path<String>,
      ) {
          // for the request `/foo/bar/baz` the value of `params` will be `bar/baz`
          //
          // on 0.5 it would be `/bar/baz`
      }
      
    • fixed: Routes like /foo and /*rest are no longer considered overlapping. /foo will take priority (#1086)

      For example:

      use axum::{Router, routing::get};
      
      let app = Router::new()
          // this used to not be allowed but now just works
          .route("/foo/*rest", get(foo))
          .route("/foo/bar", get(bar));
      
      async fn foo() {}
      
      async fn bar() {}
      
    • breaking: Trailing slash redirects have been removed. Previously if you added a route for /foo, axum would redirect calls to /foo/ to /foo (or vice versa for /foo/). That is no longer supported and such requests will now be sent to the fallback. Consider using axum_extra::routing::RouterExt::route_with_tsr if you want the old behavior (#1119)

      For example:

      use axum::{Router, routing::get};
      
      let app = Router::new()
          // a request to `GET /foo/` will now get `404 Not Found`
          // whereas in 0.5 axum would redirect to `/foo`
          //
          // same goes the other way if you had the route `/foo/`
          // axum will no longer redirect from `/foo` to `/foo/`
          .route("/foo", get(handler));
      
      async fn handler() {}
      
    • breaking: Router::fallback now only accepts Handlers (similarly to what get, post, etc accept). Use the new Router::fallback_service for setting any Service as the fallback (#1155)

      This fallback on 0.5:

      use axum::{Router, handler::Handler};
      
      let app = Router::new().fallback(fallback.into_service());
      
      async fn fallback() {}
      

      Becomes this in 0.6

      use axum::Router;
      
      let app = Router::new().fallback(fallback);
      
      async fn fallback() {}
      
    • breaking: Allow Error: Into<Infallible> for Route::{layer, route_layer} (#924)

    • breaking: MethodRouter now panics on overlapping routes (#1102)

    • breaking: Router::route now only accepts MethodRouters created with get, post, etc. Use the new Router::route_service for routing to any Services (#1155)

    Extractors

    • added: Added new type safe State extractor. This can be used with Router::with_state and gives compile errors for missing states, whereas Extension would result in runtime errors (#1155)

      We recommend migrating from Extension to State since that is more type safe and faster. That is done by using Router::with_state and State.

      This setup in 0.5

      use axum::{routing::get, Extension, Router};
      
      let app = Router::new()
          .route("/", get(handler))
          .layer(Extension(AppState {}));
      
      async fn handler(Extension(app_state): Extension<AppState>) {}
      
      #[derive(Clone)]
      struct AppState {}
      

      Becomes this in 0.6 using State:

      use axum::{routing::get, extract::State, Router};
      
      let app = Router::with_state(AppState {})
          .route("/", get(handler));
      
      async fn handler(State(app_state): State<AppState>) {}
      
      #[derive(Clone)]
      struct AppState {}
      

      If you have multiple extensions you can use fields on AppState and implement FromRef:

      use axum::{extract::{State, FromRef}, routing::get, Router};
      
      let state = AppState {
          client: HttpClient {},
          database: Database {},
      };
      
      let app = Router::with_state(state).route("/", get(handler));
      
      async fn handler(
          State(client): State<HttpClient>,
          State(database): State<Database>,
      ) {}
      
      #[derive(Clone)]
      struct AppState {
          client: HttpClient,
          database: Database,
      }
      
      #[derive(Clone)]
      struct HttpClient {}
      
      impl FromRef<AppState> for HttpClient {
          fn from_ref(state: &AppState) -> Self {
              state.client.clone()
          }
      }
      
      #[derive(Clone)]
      struct Database {}
      
      impl FromRef<AppState> for Database {
          fn from_ref(state: &AppState) -> Self {
              state.database.clone()
          }
      }
      
    • breaking: It is now only possible for one extractor per handler to consume the request body. In 0.5 doing so would result in runtime errors but in 0.6 it is a compile error (#1272)

      axum enforces this by only allowing the last extractor to consume the request.

      For example:

      use axum::{Json, http::HeaderMap};
      
      // This won't compile on 0.6 because both `Json` and `String` need to consume
      // the request body. You can use either `Json` or `String`, but not both.
      async fn handler_1(
          json: Json<serde_json::Value>,
          string: String,
      ) {}
      
      // This won't work either since `Json` is not the last extractor.
      async fn handler_2(
          json: Json<serde_json::Value>,
          headers: HeaderMap,
      ) {}
      
      // This works!
      async fn handler_3(
          headers: HeaderMap,
          json: Json<serde_json::Value>,
      ) {}
      

      This is done by reworking the FromRequest trait and introducing a new FromRequestParts trait.

      If your extractor needs to consume the request body then you should implement FromRequest, otherwise implement FromRequestParts.

      This extractor in 0.5:

      struct MyExtractor { /* ... */ }
      
      impl<B> FromRequest<B> for MyExtractor
      where
          B: Send,
      {
          type Rejection = StatusCode;
      
          async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
              // ...
          }
      }
      

      Becomes this in 0.6:

      use axum::{
          extract::{FromRequest, FromRequestParts},
          http::{StatusCode, Request, request::Parts},
      };
      
      struct MyExtractor { /* ... */ }
      
      // implement `FromRequestParts` if you don't need to consume the request body
      impl<S> FromRequestParts<S> for MyExtractor
      where
          S: Send + Sync,
      {
          type Rejection = StatusCode;
      
          async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
              // ...
          }
      }
      
      // implement `FromRequest` if you do need to consume the request body
      impl<S, B> FromRequest<S, B> for MyExtractor
      where
          S: Send + Sync,
          B: Send + 'static,
      {
          type Rejection = StatusCode;
      
          async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
              // ...
          }
      }
      
    • breaking: RequestParts has been removed as part of the FromRequest rework (#1272)

    • breaking: BodyAlreadyExtracted has been removed (#1272)

    • breaking: The following types or traits have a new S type param which represents the state (#1155):

      • Router, defaults to ()
      • MethodRouter, defaults to ()
      • FromRequest, no default
      • Handler, no default
    • added: Add RequestExt and RequestPartsExt which adds convenience methods for running extractors to http::Request and http::request::Parts (#1301)

    Middleware

    • breaking: Remove extractor_middleware which was previously deprecated. Use axum::middleware::from_extractor instead (#1077)
    • added: Support running extractors on middleware::from_fn functions (#1088)
    • added: Support any middleware response that implements IntoResponse (#1152)
    • breaking: Require middleware added with Handler::layer to have Infallible as the error type (#1152)

    Misc

    • changed: axum's MSRV is now 1.60 (#1239)
    • changed: For methods that accept some S: Service, the bounds have been relaxed so the response type must implement IntoResponse rather than being a literal Response
    • fixed: Annotate panicking functions with #[track_caller] so the error message points to where the user added the invalid route, rather than somewhere internally in axum (#1248)
    • added: Add ServiceExt with methods for turning any Service into a MakeService similarly to Router::into_make_service (#1302)

    </details>

    Open source →
  49. 0.5.17 20 Oct 2022

    Nothing published for this version

  50. 0.5.16 10 Sep 2022
    Release notes

    Security

    • breaking: Added default limit to how much data Bytes::from_request will consume. Previously it would attempt to consume the entire request body without checking its length. This meant if a malicious peer sent an large (or infinite) request body your server might run out of memory and crash.

      The default limit is at 2 MB and can be disabled by adding the new DefaultBodyLimit::disable() middleware. See its documentation for more details.

      This also applies to these extractors which used Bytes::from_request internally:

      • Form
      • Json
      • String

      (#1346)

    Open source →
  51. 0.5.15 09 Aug 2022
    Release notes
    • fixed: Don't expose internal type names in QueryRejection response. (#1171)
    • fixed: Improve performance of JSON serialization (#1178)
    • fixed: Improve build times by generating less IR (#1192)
    Open source →
  52. 0.5.14 25 Jul 2022 withdrawn
    Release notes

    Yanked, as it contained an accidental breaking change.

    Open source →
  53. 0.5.13 15 Jul 2022
    Release notes
    • fixed: If WebSocketUpgrade cannot upgrade the connection it will return a WebSocketUpgradeRejection::ConnectionNotUpgradable rejection (#1135)
    • changed: WebSocketUpgradeRejection has a new variant ConnectionNotUpgradable variant (#1135)
    Open source →
  54. 0.5.12 10 Jul 2022
    Release notes
    • added: Added debug_handler which is an attribute macro that improves type errors when applied to handler function. It is re-exported from axum-macros (#1144)
    Open source →
  55. 0.5.11 02 Jul 2022
    Release notes
    • added: Implement TryFrom<http::Method> for MethodFilter and use new NoMatchingMethodFilter error in case of failure (#1130)
    • added: Document how to run extractors from middleware (#1140)
    Open source →
  56. 0.5.10 28 Jun 2022
    Release notes
    • fixed: Make Router cheaper to clone (#1123)
    • fixed: Fix possible panic when doing trailing slash redirect (#1124)
    Open source →
  57. 0.5.9 20 Jun 2022
    Release notes
    • fixed: Fix compile error when the headers is enabled and the form feature is disabled (#1107)
    Open source →
  58. 0.5.8 18 Jun 2022
    Release notes
    • added: Support resolving host name via Forwarded header in Host extractor (#1078)
    • added: Implement IntoResponse for Form (#1095)
    • changed: axum's MSRV is now 1.56 (#1098)
    Open source →
  59. 0.5.7 08 Jun 2022
    Release notes
    • added: Implement Default for Extension (#1043)
    • fixed: Support deserializing Vec<(String, String)> in extract::Path<_> to get vector of key/value pairs (#1059)
    • added: Add extract::ws::close_code which contains constants for close codes (#1067)
    • fixed: Use impl IntoResponse less in docs (#1049)
    Open source →
  60. 0.5.6 16 May 2022
    Release notes

    Improve error messages with #[diagnostic::do_not_recommend].

    Open source →
    Release notes
    • added: Add WebSocket::protocol to return the selected WebSocket subprotocol, if there is one. (#1022)
    • fixed: Improve error message for PathRejection::WrongNumberOfParameters to hint at using Path<(String, String)> or Path<SomeStruct> (#1023)
    • fixed: PathRejection::WrongNumberOfParameters now uses 500 Internal Server Error since it's a programmer error and not a client error (#1023)
    • fixed: Fix InvalidFormContentType mentioning the wrong content type
    Open source →

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive