Cache, fetch,
render. One path.
gpui-query wires network, cache, retry, and revalidation into one small API for GPUI applications.
observer attached · watching "users"
// capabilities
gpui-query covers the fetch lifecycle
The TanStack Query feature set, rebuilt around GPUI's entity and async model.
Declarative Queries
One hook. gpui-query handles fetching, caching, and state updates so your render code stays a pure function of data.
Cache Policies
Pick one per query.
Mutations
Success and error callbacks, plus optimistic updates that roll back on failure.
Infinite Queries
Paginate with built-in infinite queries and bidirectional fetching.
Cancellation
Signal-checked retries and cooperative cancellation. No orphaned tasks.
Persistence
Serialize and restore query state across launches with custom persistence backends.
QueryClient, live
Entries age, go stale, revalidate, and log each transition. Use the row controls to force the lifecycle.
event log
- T+000.0 observers registered · 3
- T+000.0 QueryClient::new() · gc 300s · policy swr
// surface area
Three hooks. That's the API.
Queries, mutations, and pagination share the same shape: a key, an async function, and your context. Everything returns an Entity you read in render.
let (users, _sub) = use_query( QueryOptions::new("users") .cache_policy(CachePolicy::StaleWhileRevalidate { ttl_ms: 60_000, stale_ms: 300_000, }) .retry_policy(RetryPolicy::new(3).with_exponential_backoff()), |signal| async move { fetch_users(&signal).await }, cx,);let (create, _sub) = use_mutation((), cx);mutate_with_callbacks( &create, NewUser { name: "Alice" }, |vars| async move { create_user(vars).await }, MutationCallbacks::new() .on_success(|_| { /* invalidate "users" */ }) .on_error(|err| eprintln!("failed: {err:?}")), cx,);let (feed, _sub) = use_infinite_query( InfiniteQueryOptions::new(QueryKey::from(["feed"])) .max_pages(Some(10)), |last_page| async move { let cursor = last_page.map(|p| p.cursor()); let page = fetch_page(cursor).await?; Ok((page.items, page.has_more)) }, cx,);// diff
The code you stop writing
One feature, written both ways: a fetched, cached user list. Toggle to compare.
1struct UserList {2 users: Option<Vec<User>>,3 error: Option<String>,4 loading: bool,5 generation: u64,6}78impl UserList {9 fn fetch(&mut self, cx: &mut Context<Self>) {10 self.loading = true;11 self.generation += 1;12 let generation = self.generation;13 cx.spawn(async move |this, cx| {14 let result = fetch_users().await;15 this.update(cx, |this, cx| {16 if this.generation != generation {17 return; // superseded by a newer request18 }19 this.loading = false;20 match result {21 Ok(users) => this.users = Some(users),22 Err(err) => this.error = Some(err.to_string()),23 }24 cx.notify();25 })26 })27 .detach();28 }29}3031// still missing: caching, ttl, retry, dedup across views…1let (users, _sub) = use_query(2 "users",3 |signal| async move { fetch_users(&signal).await },4 cx,5);67// cached · retried · deduped · revalidated · cancellable▸ 31 lines of lifecycle plumbing → 7 lines, with more behavior.
// comparison
gpui-query vs. hand-rolled async state
| Feature | gpui-query | cx.spawn() | Raw Futures |
|---|---|---|---|
| Caching | |||
| Auto Retry | |||
| Deduplication | |||
| Cache Policies | |||
| DevTools | |||
| Persistence | |||
| Type Safety | |||
| No Setup Required | |||
| Zero Dependencies |
FIG. 1: anatomy of a query · hover a callout
Every line, accounted for
let (users, _sub) = use_query( QueryOptions::new("users")⟨A⟩ .cache_policy(CachePolicy::StaleWhileRevalidate {⟨B⟩ ttl_ms: 60_000, stale_ms: 300_000, }) .retry_policy(RetryPolicy::new(3)⟨C⟩ .with_exponential_backoff()), |signal| async move {⟨D⟩ fetch_users(&signal).await }, cx,⟨E⟩);// architecture
Three layers, one direction
Each layer has a single responsibility. Data flows down, results flow back up.
Hook Layer
use_query / use_mutation / use_infinite_query
Client Layer
QueryClient / Registry / GC
Core Layer
QueryResource / CachePolicy / QueryKey
Ship the lifecycle once
Add the crate, keep your views small, and let QueryClient own the async state machine.