Installation
gpui-query is a Cargo crate. This page covers adding it to a GPUI project, choosing the right feature flags, and installing a QueryClient as a GPUI Global so the hooks can share a single cache.
Add the dependency
Run cargo add in your crate:
cargo add gpui-query
or add it by hand to Cargo.toml:
[dependencies]
gpui-query = "0.1"
gpui-query depends on gpui as a workspace dependency. It does not ship gpui itself — your app already brings gpui in, and gpui-query links against the same version.
Feature flags
The crate is split into three layers, each behind a feature flag:
| Feature | Default | Pulls in | What it gives you |
|---|---|---|---|
core | no | serde only | The transport-agnostic state machine (QueryResource, CachePolicy, …) with no GPUI dependency. Usable in non-GPUI code or tests. |
client | yes | core + gpui | The QueryClient registry and its type-partitioned buckets. |
hook | no | client | The use_query / use_mutation hooks you call from views. |
client is on by default, so cargo add gpui-query is enough to get the registry. To use the ergonomic hooks from your components, enable the hook feature:
[dependencies]
gpui-query = { version = "0.1", features = ["hook"] }
cargo add gpui-query --features hook
You almost always want hook in an application. Reach for core alone when you need the state machine without a GPUI dependency (a library, a CLI that reasons about cached state, or unit tests).
Set up the QueryClient
QueryClient is a GPUI Global. Install it once during app setup — from then on, every hook routes resource creation through it for shared caching, deduplication, and garbage collection.
use gpui_query::client::QueryClient;
use gpui_query::{CachePolicy, core::RequestPolicy};
fn setup_app(cx: &mut gpui::App) {
cx.set_global(QueryClient::new(
// Default freshness for every query.
CachePolicy::Ttl { ttl_ms: 60_000 },
// Default behavior for concurrent fetches on the same resource.
RequestPolicy::LatestWins,
));
}
QueryClient::new takes the default cache and request policies. Tune the garbage-collection window and the default mutation retry policy with builders:
use gpui_query::client::QueryClient;
use gpui_query::{CachePolicy, RetryPolicy, core::RequestPolicy};
let client = QueryClient::new(
CachePolicy::Ttl { ttl_ms: 60_000 },
RequestPolicy::LatestWins,
)
.with_gc_time(600_000) // 10 minutes (default is 5)
.with_retry_policy(RetryPolicy::no_retries()); // default retry for mutations
cx.set_global(client);
If a hook runs before a QueryClient is installed, it falls back to creating a standalone entity — there is no shared cache, no deduplication, and no GC. Always install the client during app startup.
Verify it is reachable
From any context, read the client back to confirm the global is set:
# use gpui_query::client::QueryClient;
# fn doc(cx: &gpui::App) {
let _client = cx.global::<QueryClient>();
# }
That is the entire setup: add the crate, enable hook, install a QueryClient global. With that in place, the Quick Start shows your first end-to-end query.
Next steps
- Quick Start — define a fetcher, call
use_query, render data. - Queries — the full
use_querysurface andQueryResourceaccessors. - Caching —
CachePolicy, deduplication, and GC in depth.