| Crates.io | rs-query |
| lib.rs | rs-query |
| version | 0.0.1 |
| created_at | 2025-11-29 01:54:03.236261+00 |
| updated_at | 2025-11-29 01:54:03.236261+00 |
| description | TanStack Query-inspired async state management for GPUI |
| homepage | https://dreamstack-us.github.io/rs-query |
| repository | https://github.com/DreamStack-us/rs-query |
| max_upload_size | |
| id | 1956266 |
| size | 213,192 |
TanStack Query-inspired async state management for GPUI
A note on this project's origins:
This library was built by a developer with minimal Rust experience, with significant assistance from Claude (Opus 4.5). It's an honest attempt to bring TanStack Query patterns to the GPUI ecosystem.
This is not production-ready. It's an early alpha (v0.0.1) that needs:
- More comprehensive unit tests (aspiring to follow standards set by
tokio,serde, etc.)- Real-world battle testing
- API refinement based on community feedback
Contributions, criticism, and feedback are warmly welcomed. If you're an experienced Rustacean, your guidance would be invaluable. Open an issue, submit a PR, or just tell us what we got wrong.
spawn_query(cx, &client, &query, |this, state, cx| {
match state {
QueryState::Success(users) => this.users = users,
QueryState::Error { error, .. } => this.error = Some(error),
_ => {}
}
cx.notify();
});
spawn_query / spawn_mutation — One-liner async execution with automatic state management[dependencies]
rs-query = "0.0.1"
use rs_query::{QueryClient, QueryKey, QueryState, Query, spawn_query};
use gpui::*;
struct MyView {
client: QueryClient,
users: Vec<User>,
loading: bool,
}
impl MyView {
fn fetch_users(&mut self, cx: &mut Context<Self>) {
let query = Query::new(
QueryKey::new("users"),
|| async { fetch_users_from_api().await }
);
spawn_query(cx, &self.client, &query, |this, state, cx| {
match state {
QueryState::Loading => {
this.loading = true;
}
QueryState::Success(users) => {
this.users = users;
this.loading = false;
}
QueryState::Error { error, retry_count } => {
eprintln!("Failed after {} retries: {}", retry_count, error);
this.loading = false;
}
QueryState::Stale(users) => {
// Show stale data while refetching
this.users = users;
}
}
cx.notify();
});
}
}
use rs_query::{Mutation, MutationState, spawn_mutation};
let mutation = Mutation::new(|user: CreateUser| async move {
api::create_user(user).await
});
spawn_mutation(cx, &client, &mutation, new_user, |this, state, cx| {
match state {
MutationState::Success(user) => {
this.users.push(user);
// Invalidate related queries
this.client.invalidate(&QueryKey::new("users"));
}
MutationState::Error(e) => this.error = Some(e),
_ => {}
}
cx.notify();
});
Hierarchical keys for cache management:
// Simple key
let key = QueryKey::new("users");
// Nested key
let key = QueryKey::new("users").push("123").push("posts");
// Invalidate all user queries
client.invalidate(&QueryKey::new("users"));
use rs_query::{Query, QueryOptions, RetryConfig, RefetchOnMount};
let query = Query::new(QueryKey::new("users"), fetch_users)
.with_options(
QueryOptions::new()
.stale_time(Duration::from_secs(60))
.cache_time(Duration::from_secs(300))
.retry(RetryConfig::new(3).with_base_delay(Duration::from_millis(500)))
.refetch_on_mount(RefetchOnMount::Stale)
);
If you're building GPUI apps with async data fetching, you've probably written this pattern dozens of times:
cx.spawn(|this, mut cx| async move {
let result = fetch_data().await;
this.update(&mut cx, |this, cx| {
this.data = result;
cx.notify();
});
});
rs-query handles loading states, caching, retries, and cache invalidation — so you can focus on your app.
This project needs your help! Whether you're:
...we'd love to hear from you. See CONTRIBUTING.md or open an issue.
MIT — Built by DreamStack