leptos-query-rs 0.5.1

A powerful, type-safe data fetching and caching library for Leptos 0.8 applications
Documentation

Leptos Query

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!

Features

  • 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

Quick Start

Installation

Add to your Cargo.toml:

[dependencies]
leptos-query = "0.4.0"
leptos = "0.8"
serde = { version = "1.0", features = ["derive"] }

Basic Usage

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>
    }
}

Setup

Wrap your app with the QueryClientProvider:

#[component]
fn App() -> impl IntoView {
    view! {
        <QueryClientProvider>
            <UserProfile user_id=1/>
        </QueryClientProvider>
    }
}

API Reference

Query Hook

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 a QueryKey for caching
  • query_fn: Async function that fetches the data
  • options: Configuration options for the query

Returns:

  • QueryResult<T>: Object containing data, loading state, and actions

Query Options

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));

Query Result

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
}

Mutation Hook

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 { /* ... */ });

Error Handling

The library provides comprehensive error handling:

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum QueryError {
    NetworkError(String),
    SerializationError(String),
    DeserializationError(String),
    TimeoutError(String),
    GenericError(String),
}

Retry Configuration

let retry_config = RetryConfig::new(3, Duration::from_secs(1))
    .with_max_delay(Duration::from_secs(30))
    .with_fixed_delay()
    .no_network_retry();

Advanced Features

Query Keys

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()

Cache Invalidation

// Invalidate specific queries
client.remove_query(&QueryKey::from("users"));

// Invalidate by pattern
client.invalidate_queries(&QueryKeyPattern::Prefix(QueryKey::from("users")));

Background Refetching

let options = QueryOptions::default()
    .with_refetch_interval(Duration::from_secs(30));

Examples

See the examples/ directory for complete working examples:

  • basic.rs: Basic query usage
  • mutations.rs: Mutation examples
  • caching.rs: Advanced caching examples

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.