Queries and Consistency
Use queries and consistency with the current published Askr packages.
@askrjs/askr/data0.0.59
How to use queries and consistency
Start with this complete queries and consistency shape, then replace the example data and application service with your own.
- Import the published @askrjs/askr/data entrypoint.
- Keep queries and consistency configuration next to the component, route, or server composition root that owns it.
- Handle pending, unavailable, cancellation, and failure states, then verify the production path.
const project = defineQuery({
key: ({ id }: { id: string }) => 'project:' + id,
fetch: ({ id, signal }) => api.projects.get(id, { signal }),
});
const result = createQuery(project, { id: projectId });Define a query
defineQuery() takes a QueryDefinition — a key(input) function that derives a cache key, and a fetch(context) function that does the actual request — and hands back a reusable definition you can pass to createQuery(), serveQuery(), or prefetchQuery(). createQuery() also accepts a plain QueryOptions object directly if you don't need the input/key(input) split. Optional isConsistent() and reconcile() functions on the definition let you decide whether freshly fetched data actually replaces what's cached.
Consistency modes
Instead of a single isLoading flag, a Query<T>'s consistency field is one of 'fresh', 'stale', 'refreshing', or 'pending-write', and each value comes with a matching shape for data, error, and stale flags — a refreshing query still has its previous data available, for instance, while a stale query with an error has both data: null and staleReason: 'error'. Reading consistency first tells you which of those shapes you're dealing with before you touch data or error directly.
Query scopes
queryScope(namespace) returns a QueryScope with key() and prefix() helpers that build namespaced cache keys, plus its own invalidate(parts, options) method scoped to that namespace. This is useful once query keys get structured — a scope per resource type keeps key-building consistent and keeps invalidation calls from accidentally matching keys that belong to a different feature.
Refresh behavior
Every Query<T> carries a refresh() method from QueryControls that re-runs the fetch and returns a promise, independent of whatever caused the original fetch. Whether the refreshed data actually replaces the cached value depends on the definition's reconcile() function, if one is supplied — it gets the new data plus the key and can accept or reject it, which is how a query avoids clobbering good data with a response that arrived out of order.