axum_typed_routing/
lib.rs

1#![doc = include_str!("../README.md")]
2//!
3//! ## Basic usage
4//! The following example demonstrates the basic usage of the library.
5//! On top of any regular handler, you can add the [`route`] macro to create a typed route.
6//! Any path- or query-parameters in the url will be type-checked at compile-time, and properly
7//! extracted into the handler.
8//!
9//! The following example shows how the path parameter `id`, and query parameters `amount` and
10//! `offset` are type-checked and extracted into the handler.
11//!
12//! ```
13#![doc = include_str!("../examples/basic.rs")]
14//! ```
15//!
16//! Some valid url's as get-methods are:
17//! - `/item/1?amount=2&offset=3`
18//! - `/item/1?amount=2`
19//! - `/item/1?offset=3`
20//! - `/item/500`
21//!
22//! By marking the `amount` and `offset` parameters as `Option<T>`, they become optional.
23//!
24//! ## Example with `aide`
25//! When the `aide` feature is enabled, it's possible to automatically generate OpenAPI
26//! documentation for the routes. The [`api_route`] macro is used in place of the [`route`] macro.
27//!
28//! Please read the [`aide`] documentation for more information on usage.
29//! ```
30#![doc = include_str!("../examples/aide.rs")]
31//! ```
32
33use axum::routing::MethodRouter;
34
35type TypedHandler<S = ()> = fn() -> (&'static str, MethodRouter<S>);
36pub use axum_typed_routing_macros::route;
37
38/// A trait that allows typed routes, created with the [`route`] macro to
39/// be added to an axum router.
40///
41/// Typed handlers are of the form `fn() -> (&'static str, MethodRouter<S>)`, where
42/// `S` is the state type. The first element of the tuple is the path, and the second
43/// is the method router.
44pub trait TypedRouter: Sized {
45    /// The state type of the router.
46    type State: Clone + Send + Sync + 'static;
47
48    /// Add a typed route to the router, usually created with the [`route`] macro.
49    ///
50    /// Typed handlers are of the form `fn() -> (&'static str, MethodRouter<S>)`, where
51    /// `S` is the state type. The first element of the tuple is the path, and the second
52    /// is the method router.
53    fn typed_route(self, handler: TypedHandler<Self::State>) -> Self;
54}
55
56impl<S> TypedRouter for axum::Router<S>
57where
58    S: Send + Sync + Clone + 'static,
59{
60    type State = S;
61
62    fn typed_route(self, handler: TypedHandler<Self::State>) -> Self {
63        let (path, method_router) = handler();
64        self.route(path, method_router)
65    }
66}
67
68#[cfg(feature = "aide")]
69pub use aide_support::*;
70#[cfg(feature = "aide")]
71mod aide_support {
72    use crate::{TypedHandler, TypedRouter};
73    use aide::{
74        axum::{routing::ApiMethodRouter, ApiRouter},
75        transform::TransformPathItem,
76    };
77
78    type TypedApiHandler<S = ()> = fn() -> (&'static str, ApiMethodRouter<S>);
79
80    pub use axum_typed_routing_macros::api_route;
81
82    impl<S> TypedRouter for ApiRouter<S>
83    where
84        S: Send + Sync + Clone + 'static,
85    {
86        type State = S;
87
88        fn typed_route(self, handler: TypedHandler<Self::State>) -> Self {
89            let (path, method_router) = handler();
90            self.route(path, method_router)
91        }
92    }
93
94    /// Same as [`TypedRouter`], but with support for `aide`.
95    pub trait TypedApiRouter: TypedRouter {
96        /// Same as [`TypedRouter::typed_route`], but with support for `aide`.
97        fn typed_api_route(self, handler: TypedApiHandler<Self::State>) -> Self;
98
99        /// Same as [`TypedApiRouter::typed_api_route`], but with a custom path transform for
100        /// use with `aide`.
101        fn typed_api_route_with(
102            self,
103            handler: TypedApiHandler<Self::State>,
104            transform: impl FnOnce(TransformPathItem) -> TransformPathItem,
105        ) -> Self;
106    }
107
108    impl<S> TypedApiRouter for ApiRouter<S>
109    where
110        S: Send + Sync + Clone + 'static,
111    {
112        fn typed_api_route(self, handler: TypedApiHandler<Self::State>) -> Self {
113            let (path, method_router) = handler();
114            self.api_route(path, method_router)
115        }
116
117        fn typed_api_route_with(
118            self,
119            handler: TypedApiHandler<Self::State>,
120            transform: impl FnOnce(TransformPathItem) -> TransformPathItem,
121        ) -> Self {
122            let (path, method_router) = handler();
123            self.api_route_with(path, method_router, transform)
124        }
125    }
126}