A React Query inspired data fetching library for Leptos applications, providing powerful caching, background updates, and error handling capabilities.
π Now fully compatible with Leptos 0.8!
- Declarative Data Fetching: Write queries as simple async functions
- Automatic Caching: Built-in cache with configurable stale times
- Background Updates: Keep data fresh with background refetching
- Error Handling: Comprehensive error handling with retry logic
- Type Safety: Full type safety with Rust's type system
- WASM Compatible: Works in both native and web environments
- Leptos 0.8 Ready: Full compatibility with the latest Leptos framework
Add to your Cargo.toml
:
[dependencies]
leptos-query = "0.4.0"
leptos = "0.8"
serde = { version = "1.0", features = ["derive"] }
use leptos::*;
use leptos_query::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
async fn fetch_user(id: u32) -> Result<User, QueryError> {
// Your async function here
Ok(User {
id,
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
})
}
#[component]
fn UserProfile(user_id: u32) -> impl IntoView {
let user_query = use_query(
move || QueryKey::new(&["user", &user_id.to_string()]),
move || async move { fetch_user(user_id).await },
QueryOptions::default(),
);
view! {
<div>
{move || match user_query.data.get() {
Some(user) => view! { <div>"User: " {user.name}</div> },
None if user_query.is_loading.get() => view! { <div>"Loading..."</div> },
None => view! { <div>"No user found"</div> },
}}
</div>
}
}
Wrap your app with the QueryClientProvider
:
#[component]
fn App() -> impl IntoView {
view! {
<QueryClientProvider>
<UserProfile user_id=1/>
</QueryClientProvider>
}
}
pub fn use_query<T, F, Fut>(
key_fn: F,
query_fn: impl Fn() -> Fut + Clone + Send + Sync + 'static,
options: QueryOptions,
) -> QueryResult<T>
Parameters:
key_fn
: Function that returns aQueryKey
for cachingquery_fn
: Async function that fetches the dataoptions
: Configuration options for the query
Returns:
QueryResult<T>
: Object containing data, loading state, and actions
let options = QueryOptions::default()
.with_stale_time(Duration::from_secs(60))
.with_cache_time(Duration::from_secs(300))
.with_refetch_interval(Duration::from_secs(30));
pub struct QueryResult<T> {
pub data: Signal<Option<T>>, // The query data
pub error: Signal<Option<QueryError>>, // Error if any
pub is_loading: Signal<bool>, // Whether loading
pub is_success: Signal<bool>, // Whether succeeded
pub is_error: Signal<bool>, // Whether failed
pub status: Signal<QueryStatus>, // Current status
pub refetch: Callback<()>, // Refetch function
}
pub fn use_mutation<TData, TError, TVariables, F, Fut>(
mutation_fn: F,
options: MutationOptions,
) -> MutationResult<TData, TError, TVariables>
Example:
async fn create_user(user: CreateUserRequest) -> Result<User, QueryError> {
// Your mutation logic here
Ok(User { /* ... */ })
}
let mutation = use_mutation(
create_user,
MutationOptions::default()
.invalidate_queries(vec![QueryKeyPattern::Exact(QueryKey::from("users"))]),
);
// Execute mutation
mutation.mutate.call(CreateUserRequest { /* ... */ });
The library provides comprehensive error handling:
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum QueryError {
NetworkError(String),
SerializationError(String),
DeserializationError(String),
TimeoutError(String),
GenericError(String),
}
let retry_config = RetryConfig::new(3, Duration::from_secs(1))
.with_max_delay(Duration::from_secs(30))
.with_fixed_delay()
.no_network_retry();
Query keys are used for caching and invalidation:
// Simple key
QueryKey::from("users")
// Compound key
QueryKey::from(["users", user_id.to_string()])
// With parameters
QueryKey::from_parts(&[user_id, filter]).unwrap()
// Invalidate specific queries
client.remove_query(&QueryKey::from("users"));
// Invalidate by pattern
client.invalidate_queries(&QueryKeyPattern::Prefix(QueryKey::from("users")));
let options = QueryOptions::default()
.with_refetch_interval(Duration::from_secs(30));
leptos-query-rs is inspired by TanStack Query but built for the Rust/Leptos ecosystem. See our comprehensive comparison for a detailed analysis.
Key advantages of leptos-query-rs:
- β Compile-time type safety with Rust's type system
- β Local-first architecture with CRDT-based conflict resolution
- β Native SSR support without hydration complexity
- β WebAssembly performance with zero-cost abstractions
- β Memory safety preventing entire classes of runtime errors
When to choose each:
- TanStack Query: React applications, mature ecosystem, extensive community
- leptos-query-rs: Leptos applications, type safety, performance, offline-first
See the examples/
directory for complete working examples:
basic.rs
: Basic query usagemutations.rs
: Mutation examplescaching.rs
: Advanced caching examples
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.