simple-svelte-query
v0.1.0
Published
Promise-first query/cache library for Svelte 5 inspired by TanStack Query
Maintainers
Readme
simple-svelte-query
Query/cache library for Svelte 5 inspired by TanStack Query, with one intentional architectural decision:
- similarity: hierarchical keys, stale time, invalidation by key/prefix
- difference: it caches the
Promiseitself, not only the resolved value
This reduces boilerplate in flows that use native Svelte await, while keeping async semantics explicit.
Installation
bun add simple-svelte-query
npm install simple-svelte-query
pnpm add simple-svelte-query
yarn add simple-svelte-queryRequirements
- Svelte 5
- For direct
awaitexpressions in<script>/markup, enablecompilerOptions.experimental.async = true - Without async mode, consume queries with
{#await query}instead
Quick start
import { QueryClient } from 'simple-svelte-query';
const queryClient = new QueryClient();<script lang="ts">
import { QueryClient } from 'simple-svelte-query';
const queryClient = new QueryClient();
let search = $state('phone');
const productsQuery = queryClient.createQuery(() => ({
queryKey: ['products', search],
queryFn: ({ signal }) =>
fetch(`/api/products?q=${encodeURIComponent(search)}`, { signal }).then((r) => r.json())
}));
</script>
<ul style:opacity={productsQuery.pending ? 0.4 : 1}>
{#each (await productsQuery).items as item}
<li>{item.name}</li>
{/each}
</ul>API
Detailed reference: docs/API.md.
Public exports
QueryClientqueryOptions
new QueryClient(options?)
options.staleTime?: defines the defaultstaleTime(ms)options.hashKey?: function to hashqueryKeyinto internal string key (defaultJSON.stringify)options.persist?: optional persistence configoptions.persist.persister: required whenpersistexists; must implementget,set,del,clearoptions.persist.hydrate?:(value) => Promise<value>transformation before using persisted valuesoptions.persist.dehydrate?:(queryKey, value) => persistedValue | undefined; when it returnsundefined, nothing is saved- expected shape:
new QueryClient({
persist: {
persister,
hydrate,
dehydrate
}
});staleTimeandhashKeyare instance defaults scoped to thatQueryClient
createQuery(() => options)
- creates a reactive query
- returns a
PromiseLike<T>object withqueryKeyandpending - uses
hydratableinternally for SSR/hydration - works with
{#await query}everywhere, or with directawait querywhen Svelte async mode is enabled
fetchQuery(options)
- fetches and populates cache
- respects
staleTime(refetch only when stale)
ensureQuery(options)
- ensures a cache entry without checking staleness
setQuery(queryKey, value)
- injects a value or
Promiseinto cache, wrapped withPromise.resolve(value)
removeQuery(queryKey)
- removes one cache entry using the original
queryKey - works with custom
hashKey, because the client computes the internal cache key for you
getQuery(queryKey)
- returns the cached
Promise(orundefined)
invalidateQuery(queryKey)
- exact invalidation by key
invalidateQueries(prefix?)
- prefix-based invalidation; with no args invalidates everything
- works with custom
hashKeybecause prefixes are tracked per cached entry
clear()
- clears all cached queries
queryOptions(options)
- identity helper to preserve
queryKeyliteral types and propagate them toqueryFn
Code examples
createQuery (reactive)
const productsQuery = queryClient.createQuery(() => ({
queryKey: ['products', filters],
queryFn: ({ signal }) => fetchProducts(filters, signal)
}));
// useful for dimming stale data while the next key settles
const style = productsQuery.pending ? 0.4 : 1;
const products = await productsQuery;queryOptions (typed options helper)
const userQueryOptions = queryOptions({
queryKey: ['user', userId] as const,
queryFn: ({ queryKey: [, id], signal }) =>
fetch(`/api/users/${id}`, { signal }).then((r) => r.json())
});
const user = await queryClient.fetchQuery(userQueryOptions);fetchQuery (imperative)
const user = await queryClient.fetchQuery({
queryKey: ['user', userId],
queryFn: ({ signal }) => fetch(`/api/users/${userId}`, { signal }).then((r) => r.json())
});ensureQuery (does not revalidate staleness)
const postsPromise = queryClient.ensureQuery({
queryKey: ['posts', userId],
queryFn: ({ signal }) => fetch(`/api/posts?user=${userId}`, { signal }).then((r) => r.json())
});
const posts = await postsPromise;setQuery + getQuery (seed/optimistic cache)
queryClient.setQuery(['user', userId], { id: userId, name: 'Optimistic name' });
queryClient.setQuery(['user', userId], Promise.resolve({ id: userId, name: 'From promise' }));
const cachedUser = await queryClient.getQuery<{ id: string; name: string }>(['user', userId]);clear (drop all cached queries)
queryClient.clear();Invalidation by exact key and by prefix
queryClient.invalidateQuery(['user', userId]); // exact
queryClient.invalidateQueries(['users']); // all keys starting with ['users', ...]
queryClient.invalidateQueries(); // allcreateQuery with reactive key switching
<script lang="ts">
import { QueryClient } from 'simple-svelte-query';
const queryClient = new QueryClient();
let category = $state('smartphones');
const productsQuery = queryClient.createQuery(() => ({
queryKey: ['products', category],
queryFn: ({ signal }) =>
fetch(`/api/products?category=${category}`, { signal }).then((r) => r.json())
}));
</script>
<button onclick={() => (category = 'laptops')}>Switch category</button>
<div style:opacity={productsQuery.pending ? 0.4 : 1}>
{#each (await productsQuery).items as item}
<div>{item.name}</div>
{/each}
</div>Similarities and differences vs TanStack Query
Similarities
- hierarchical
queryKeymodel - precise and batch invalidation
staleTime- cancellation support via
AbortSignal
Intentional differences
- no
useQuery, no large state object (isFetching,status, etc) - direct consumption through
awaitin component/script - promise-first cache, useful for async composition and predictable concurrent behavior
Scripts
bun run dev
bun run check
bun run lint
bun run test
bun run buildPublishing
bun run prepack
npm publishLinks
- X: https://x.com/Thiagolinog
- Bluesky: https://bsky.app/profile/thiagolino8.bsky.social
- LinkedIn: https://www.linkedin.com/in/thiago-lino-gomes-5812581bb
- GitHub: https://github.com/Thiagolino8/simple-svelte-query
- npm package: https://www.npmjs.com/package/simple-svelte-query
License
MIT
