Query
query() absorbs the loading / error / cache / refetch boilerplate of fetching data, and hands you signals wired straight into the reactivity graph.
query
Pass a cache-key function and a fetcher. You get back data, loading, and error signals plus a refetch() method. The key is tracked — when it changes, the query refetches. Select the view with a computed() (the sanctioned multi-branch pattern) so each branch stays reactive.
import { component, query, computed, html } from '@nisli/core';
component('user-card', (props) => {
const user = query(
() => ['user', props.id.value], // cache key (tracked)
() => fetch(`/api/users/${props.id.value}`).then((r) => r.json()),
);
const view = computed(() => {
if (user.loading.value) return html`<p>Loading…</p>`;
if (user.error.value) return html`<p>Error: ${user.error.value.message}</p>`;
return html`<p>${user.data.value?.name}</p>`;
});
return html`${view}`;
});
Refetch & caching
Call user.refetch() to reload. staleTime (ms) marks cached data fresh for that window — while it is fresh, the automatic query (on mount or when the key changes) serves the cache and skips fetching. An explicit refetch() bypasses staleTime — it is never a no-op inside the fresh window — though if the same query is already in flight it joins that request rather than firing a second. Only once staleTime expires does the automatic path fetch again. Use QueryClient to prefetch.