| Crates.io | yew_server_hook |
| lib.rs | yew_server_hook |
| version | 0.3.0 |
| created_at | 2025-11-21 10:13:40.099241+00 |
| updated_at | 2025-11-21 10:31:02.473316+00 |
| description | A procedural macro that generates both server-side API handlers and client-side Yew hooks from a single function definition |
| homepage | |
| repository | https://github.com/netrondev/yew_crates |
| max_upload_size | |
| id | 1943368 |
| size | 95,597 |
A procedural macro that generates both server-side API handlers and client-side Yew hooks from a single function definition. Write your backend logic once, and automatically get both the Axum server endpoint and the Yew hook to call it.
Add this to your Cargo.toml:
cargo add yew_server_hook
use yew_server_hook::yewserverhook;
#[yewserverhook(path = "/api/hello", method = "GET")]
pub async fn get_hello() -> Result<String, String> {
Ok("Hello, World!".to_string())
}
This generates:
/api/hello (GET)use_get_hello()get_hello() for programmatic useuse yew::prelude::*;
#[function_component(HelloComponent)]
pub fn hello_component() -> Html {
let data = use_get_hello();
match &data.state {
DataState::Loading => html! { <p>{"Loading..."}</p> },
DataState::Data(message) => html! { <p>{message}</p> },
DataState::Error(err) => html! { <p class="error">{err}</p> },
DataState::Empty => html! { <p>{"No data"}</p> },
}
}
#[yewserverhook(path = "/api/users", method = "GET")]
pub async fn get_users(role: String, active: bool) -> Result<Vec<User>, String> {
// Your database logic here
let users = fetch_users_from_db(role, active).await?;
Ok(users)
}
Use in a component:
#[function_component(UsersComponent)]
pub fn users_component() -> Html {
let users = use_get_users("admin".to_string(), true);
html! {
<div>
if users.is_loading {
<p>{"Loading users..."}</p>
}
{
match &users.state {
DataState::Data(user_list) => html! {
<ul>
{ for user_list.iter().map(|user| html! {
<li>{&user.name}</li>
})}
</ul>
},
DataState::Error(err) => html! { <p>{err}</p> },
_ => html! {}
}
}
</div>
}
}
#[yewserverhook(path = "/api/users", method = "POST")]
pub async fn create_user(name: String, email: String) -> Result<User, String> {
// Your database logic here
let user = User { name, email };
save_user_to_db(&user).await?;
Ok(user)
}
// Direct call (not using the hook):
async fn handle_submit() {
match create_user("Alice".to_string(), "alice@example.com".to_string()).await {
Ok(user) => log::info!("Created user: {:?}", user),
Err(e) => log::error!("Error: {}", e),
}
}
The macro supports all standard HTTP methods:
GET - Parameters sent as query stringsPOST - Parameters sent as JSON body (default)PUT - Parameters sent as JSON bodyDELETE - Parameters sent as JSON bodyPATCH - Parameters sent as JSON bodyThe generated hook returns an ApiHook<T> struct with:
pub struct ApiHook<T> {
pub state: DataState<T>,
pub is_loading: bool, // True only on first load
pub is_updating: bool, // True on first load and subsequent updates
}
The DataState<T> enum:
pub enum DataState<T> {
Loading, // Initial state
Data(T), // Successfully loaded data
Error(String), // Error with message
Empty, // For Vec types, when response is empty
}
The crate supports server-side rendering through the ssr feature flag:
[features]
ssr = []
ssr is enabled: Server handlers are generatedssr is disabled: Client-side hooks and fetch functions are generatedRoutes are automatically registered using the inventory crate. To use the auto-registered routes:
use axum::Router;
// Routes are automatically collected and can be registered
let app = Router::new()
.merge(your_generated_routes());
Your function must:
asyncResult<T, E> where both T and E implement Serialize and DeserializeSerialize, Deserialize, and CloneFor each annotated function, the macro generates:
Parameter Struct (if function has parameters):
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FunctionNameParams {
pub param1: Type1,
pub param2: Type2,
}
Server Handler (with ssr feature):
#[cfg(feature = "ssr")]
pub async fn function_name_handler(
axum::Json(params): axum::Json<FunctionNameParams>
) -> Result<axum::Json<ReturnType>, ErrorType>
Client Hook:
#[yew::hook]
pub fn use_function_name(params...) -> ApiHook<ReturnType>
Direct Client Function:
pub async fn function_name(params...) -> Result<ReturnType, String>
Run tests with:
cargo test --tests
Licensed under either of:
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.