# Queries and Consistency

> Key queries, share results between components, and control how cached data goes stale.

Source: [https://askrjs.com/docs/data/queries-and-consistency](https://askrjs.com/docs/data/queries-and-consistency)

Status: stable. Packages: @askrjs/askr/data.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

```tsx
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 don't gate whether fetched data reaches the cache — freshly fetched data is written to the query's state either way. What they control is what happens next: isConsistent() returning false marks the result stale (consistency: 'stale', staleReason: 'inconsistent') and triggers reconcile(), whose return value decides whether the query automatically retries.

## Consistency modes

Instead of a single isLoading flag, a Query&lt;T&gt;'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 staleReason: 'error' and either retains previous data or has data: null when no successful value exists. 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&lt;T&gt; carries a refresh() method from QueryControls that re-runs the fetch and returns a promise, independent of whatever caused the original fetch. reconcile() doesn't decide whether refreshed data replaces the cached value — it's already written by the time reconcile() runs, and reconcile()'s return value only controls whether the query schedules another automatic retry. An out-of-order response arriving late is instead guarded separately, by generation and abort-controller identity checks that discard a stale in-flight fetch's result once a newer one has superseded it.

## Documentation navigation

[Previous](https://askrjs.com/docs/data/resources/index.md) | [Next](https://askrjs.com/docs/data/mutations-and-invalidation/index.md)
