Skip to content

Commit

Permalink
Improve State and Router docs (#1543)
Browse files Browse the repository at this point in the history
  • Loading branch information
jimmycuadra committed Nov 22, 2022
1 parent 7d0bb28 commit 6771729
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 40 deletions.
10 changes: 9 additions & 1 deletion axum/src/docs/routing/merge.md
Expand Up @@ -37,7 +37,15 @@ let app = Router::new()
# };
```

## Panics
# Merging routers with state

When combining [`Router`]s with this function, each [`Router`] must have the
same type of state. See ["Combining stateful routers"][combining-stateful-routers]
for details.

# Panics

- If two routers that each have a [fallback](Router::fallback) are merged. This
is because `Router` only allows a single fallback.

[combining-stateful-routers]: crate::extract::State#combining-stateful-routers
45 changes: 9 additions & 36 deletions axum/src/docs/routing/nest.md
@@ -1,4 +1,4 @@
Nest a [`Service`] at some path.
Nest a [`Router`] at some path.

This allows you to break your application into smaller pieces and compose
them together.
Expand Down Expand Up @@ -64,7 +64,7 @@ let app = Router::new().nest("/:version/api", users_api);
# };
```

# Differences to wildcard routes
# Differences from wildcard routes

Nested routes are similar to wildcard routes. The difference is that
wildcard routes still see the whole URI whereas nested routes will have
Expand Down Expand Up @@ -147,42 +147,14 @@ let app = Router::new()

Here requests like `GET /api/not-found` will go to `api_fallback`.

# Nesting a router with a different state type
# Nesting routers with state

By default `nest` requires a `Router` with the same state type as the outer
`Router`. If you need to nest a `Router` with a different state type you can
use [`Router::with_state`] and [`Router::nest_service`]:
When combining [`Router`]s with this function, each [`Router`] must have the
same type of state. See ["Combining stateful routers"][combining-stateful-routers]
for details.

```rust
use axum::{
Router,
routing::get,
extract::State,
};

#[derive(Clone)]
struct InnerState {}

#[derive(Clone)]
struct OuterState {}

async fn inner_handler(state: State<InnerState>) {}

let inner_router = Router::new()
.route("/bar", get(inner_handler))
.with_state(InnerState {});

async fn outer_handler(state: State<OuterState>) {}

let app = Router::new()
.route("/", get(outer_handler))
.nest_service("/foo", inner_router)
.with_state(OuterState {});
# let _: axum::routing::RouterService = app;
```

Note that the inner router will still inherit the fallback from the outer
router.
If you want to compose axum services with different types of state, use
[`Router::nest_service`].

# Panics

Expand All @@ -193,3 +165,4 @@ for more details.

[`OriginalUri`]: crate::extract::OriginalUri
[fallbacks]: Router::fallback
[combining-stateful-routers]: crate::extract::State#combining-stateful-routers
71 changes: 71 additions & 0 deletions axum/src/extract/state.rs
Expand Up @@ -46,6 +46,77 @@ use std::{
/// # let _: axum::routing::RouterService = app;
/// ```
///
/// ## Combining stateful routers
///
/// Multiple [`Router`]s can be combined with [`Router::nest`] or [`Router::merge`]
/// When combining [`Router`]s with one of these methods, the [`Router`]s must have
/// the same state type. Generally, this can be inferred automatically:
///
/// ```
/// use axum::{Router, routing::get, extract::State};
///
/// #[derive(Clone)]
/// struct AppState {}
///
/// let state = AppState {};
///
/// // create a `Router` that will be nested within another
/// let api = Router::new()
/// .route("/posts", get(posts_handler));
///
/// let app = Router::new()
/// .nest("/api", api)
/// .with_state(state);
///
/// async fn posts_handler(State(state): State<AppState>) {
/// // use `state`...
/// }
/// # let _: axum::routing::RouterService = app;
/// ```
///
/// However, if you are composing [`Router`]s that are defined in separate scopes,
/// you may need to annotate the [`State`] type explicitly:
///
/// ```
/// use axum::{Router, RouterService, routing::get, extract::State};
///
/// #[derive(Clone)]
/// struct AppState {}
///
/// fn make_app() -> RouterService {
/// let state = AppState {};
///
/// Router::new()
/// .nest("/api", make_api())
/// .with_state(state) // the outer Router's state is inferred
/// }
///
/// // the inner Router must specify its state type to compose with the
/// // outer router
/// fn make_api() -> Router<AppState> {
/// Router::new()
/// .route("/posts", get(posts_handler))
/// }
///
/// async fn posts_handler(State(state): State<AppState>) {
/// // use `state`...
/// }
/// # let _: axum::routing::RouterService = make_app();
/// ```
///
/// In short, a [`Router`]'s generic state type defaults to `()`
/// (no state) unless [`Router::with_state`] is called or the value
/// of the generic type is given explicitly.
///
/// It's also possible to combine multiple axum services with different state
/// types. See [`Router::nest_service`] for details.
///
/// [`Router`]: crate::Router
/// [`Router::merge`]: crate::Router::merge
/// [`Router::nest_service`]: crate::Router::nest_service
/// [`Router::nest`]: crate::Router::nest
/// [`Router::with_state`]: crate::Router::with_state
///
/// # With `MethodRouter`
///
/// ```
Expand Down
7 changes: 4 additions & 3 deletions axum/src/lib.rs
Expand Up @@ -165,11 +165,12 @@
//!
//! # Sharing state with handlers
//!
//! It is common to share some state between handlers for example to share a
//! pool of database connections or clients to other services.
//! It is common to share some state between handlers. For example, a
//! pool of database connections or clients to other services may need to
//! be shared.
//!
//! The three most common ways of doing that are:
//! - Using the [`State`] extractor.
//! - Using the [`State`] extractor
//! - Using request extensions
//! - Using closure captures
//!
Expand Down
35 changes: 35 additions & 0 deletions axum/src/routing/mod.rs
Expand Up @@ -212,6 +212,41 @@ where
}

/// Like [`nest`](Self::nest), but accepts an arbitrary `Service`.
///
/// While [`nest`](Self::nest) requires [`Router`]s with the same type of
/// state, you can use this method to combine [`Router`]s with different
/// types of state:
///
/// ```
/// use axum::{
/// Router,
/// routing::get,
/// extract::State,
/// };
///
/// #[derive(Clone)]
/// struct InnerState {}
///
/// #[derive(Clone)]
/// struct OuterState {}
///
/// async fn inner_handler(state: State<InnerState>) {}
///
/// let inner_router = Router::new()
/// .route("/bar", get(inner_handler))
/// .with_state(InnerState {});
///
/// async fn outer_handler(state: State<OuterState>) {}
///
/// let app = Router::new()
/// .route("/", get(outer_handler))
/// .nest_service("/foo", inner_router)
/// .with_state(OuterState {});
/// # let _: axum::routing::RouterService = app;
/// ```
///
/// Note that the inner router will still inherit the fallback from the outer
/// router.
#[track_caller]
pub fn nest_service<T>(self, path: &str, svc: T) -> Self
where
Expand Down

0 comments on commit 6771729

Please sign in to comment.