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}