Quick start
From zero to a cached, cancellable, auto-rendering query in three steps.
1. Declare a query and execute it#
@page "/todos"
@inherits DejaComponentBase
@inject TodoApi Api
@if (_todos.IsLoading && _todos.Data is null) { <p>Loading…</p> }
@if (_todos.IsError) { <p class="error">@_todos.ErrorMessage</p> }
<ul>
@foreach (var todo in _todos.Data ?? [])
{
<li>@todo.Title</li>
}
</ul>
@code {
private readonly Query<List<Todo>> _todos = new();
protected override Task OnInitializedAsync()
=> _todos.Execute("todos", Api.GetTodosAsync);
}Execute("todos", Api.GetTodosAsync) is the keyed shorthand: the first argument is
the cache key (a string converts implicitly to QueryKey.Of("todos")), the second
is any Func<CancellationToken, Task<T>>. Deja passes a token that is
cancelled when a newer load supersedes this one or the component is disposed.
2. Bind the state#
Everything the request lifecycle exposes is a bindable property:
IsLoading— any execution in flightIsReFetching— an execution after the first; use for subtle refresh indicators instead of a full-page spinnerIsError/ErrorMessage— the most recent failureData— the most recent resultIsCachedData/IsStale/UpdatedAt— cache facts for keyed queries
3. Write with a mutation, invalidate the key#
<input @bind="_title"/>
<button @onclick="Add" disabled="@_addTodo.IsLoading">Add</button>
@code {
private readonly Mutation<Todo> _addTodo = new();
private string _title = "";
private Task Add() => _addTodo.Execute(
token => Api.AddTodoAsync(_title, token),
p => p.InvalidateKeys = [QueryKey.Of("todos")]);
}
On success, every mounted query whose key starts with "todos" refetches in the
background — in every component showing that data. No event wiring, no manual reload calls.
See it live#
That's the whole loop. The todo list demo is exactly this pattern running against a live API, with a state inspector attached. From here:
- Queries — the full parameter surface
- The cache — what a key buys you
- Component base — what the base class does for you