svelte-querify
v1.0.0
Published
`Svelte Querify` is a **minimal**, **performant**, and **easy-to-use** library for reactive data fetching in Svelte‑5 applications. Designed with precision and simplicity.
Readme
Svelte Querify — Lightweight & Elegant Data Query Cache Library For Sveltekit 🌟
Svelte Querify is a minimal, performant, and easy-to-use library for reactive data fetching in Svelte‑5 applications. Designed with precision and simplicity.
- ✅ Fast, automatic caching and revalidation
- ✅ Reactive state for query & mutation
- ✅ No boilerplate, No ceremonies — just clean, concise API
- ✅ Fully compatible with Svelte 5 runes (
$state,$derived)
📦 Installation
npm install svelte-querifythen in your components, you can use the following:
import { QueryCache, mutation, query } from 'svelte-querify';🚀 Key Features
🔧 QueryCache.config(opts)
Set global configuration options for:
ttl(cache time in minutes, default: 10)refresh(background revalidation everynminutes, default: 5)enabled(default query behavior, default: true)
🧠 query(key, fn, options)
Reactive query builder that returns:
data— fetched resultpending— initial fetch staterefetching— background revalidate in progresserror— fetch errorrefetch()— manual refresh method
⚡ Features:
- Smart key validation (prevents invalid queries)
- Customizable cache-timing (
ttl) - Lazy revalidation (
refresh) - Debouncing during active fetches
- Data preserved across refetches
- Automatic Response/JSON handling
🔁 mutation(fn, hooks)
Handles mutations with:
data,pending,error— reactive statesubmit(vars)— execute mutationOptional lifecycle hooks:
processing(before start) - for optimistic updatessuccess(on success)failure(on error) - for rollback or notificationscompletion(always runs) - for cleanup
Example:
import { mutation } from 'svelte-querify';
const PostMutation = mutation(
(post: { title: string }) => fetch('/api/posts', { method: 'POST', body: JSON.stringify(post) }),
{
processing: (vars) => console.log('Creating post...', vars),
success: (response) => {
console.log('Post created!', response.data);
QueryCache.refresh(['posts']); // Refresh posts list
},
failure: (error) => console.error('Failed:', error.message),
completion: (response) => console.log('Request finished', response.status)
}
);
// Execute the mutation
await PostMutation.submit({ title: 'My New Post' });🔍 QueryCache.get(key)
Retrieve cached data without triggering a fetch:
const cachedUser = QueryCache.get(['user', userId]);
if (cachedUser) {
// Use cached data immediately
}🧹 QueryCache.refresh(key)
Re-execute an existing query in background (preserves current data during refetch):
QueryCache.refresh(['posts']); // Triggers background refetch🧼 QueryCache.invalidate(key, options)
Remove cached queries:
- Single query:
QueryCache.invalidate(['posts', 42]) - Bulk removal:
QueryCache.invalidate(['posts'], { bulk: true })- removes all queries starting with "posts" - Clear all:
QueryCache.invalidate()- clears entire cache
Example:
// Remove specific post
QueryCache.invalidate(['posts', 42]);
// Remove all post-related queries
QueryCache.invalidate(['posts'], { bulk: true });
// Clear everything
QueryCache.invalidate();🎯 How It Works
- Cache Map: Each query is stored by a unique key
- TTL Timer: Auto-deletes stale entries after configured time
- Refresh Timer: Triggers lazy background revalidation
- Debouncing Guard: Prevents duplicate concurrent fetches
- Reactive State: Built with Svelte
$statefor optimal performance
📋 Usage Examples
Setup Global Config (Optional)
// In your +layout.svelte or app initialization
import { QueryCache } from 'svelte-querify';
QueryCache.config({
ttl: 15, // Cache for 15 minutes
refresh: 3, // Lazy Background refresh every 3 minutes if the query is accessed again
enabled: true // Queries run immediately by default
});Basic Query
import { query } from 'svelte-querify';
// Simple query - note: don't destructure unless using $derived
const UsersQuery = query(['users'], () => fetch('/api/users'), { ttl: 5, refresh: 2 });
// In your template
{#if UsersQuery.pending}
<span>Loading users...</span>
{/if}
{#if UsersQuery.error}
<p>Error: {UsersQuery.error.message}</p>
{/if}
{#if UsersQuery.data}
<ul>
{#each UsersQuery.data as user}
<li>{user.name}</li>
{/each}
</ul>
{/if}
<!-- Manual refresh -->
<button onclick={() => UsersQuery.refetch()}>
Refresh Users
</button>Reactive Query with Parameters
// For dynamic queries (search, pagination, etc.)
let userId = $state(1);
// Wrap in $derived for reactivity when parameters change
let { data: user, pending, error, refetch } = $derived(
query(
['user', userId],
() => fetch(`/api/users/${userId}`).then(r => r.json()),
{ enabled: !!userId } // Only run when userId exists
)
);
{#if pending}
<span>Loading user...</span>
{:else if error}
<p>Error: {error.message}</p>
{:else if user}
<h1>{user.name}</h1>
<p>{user.email}</p>
{/if}Search Query Example
let searchTerm = $state('');
// Reactive search that triggers when searchTerm changes, can also be used for pagination
let searchResults = $derived(
query(
['search', searchTerm],
() => fetch(`/api/search?q=${encodeURIComponent(searchTerm)}`).then((r) => r.json()),
{
enabled: searchTerm.length > 2, // Only search with 3+ characters
ttl: 2 // Short cache for search results
}
)
);Mutation with Cache Updates
import { mutation, QueryCache } from 'svelte-querify';
const createUser = mutation(
(userData: { name: string; email: string }) =>
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
}),
{
processing: (vars) => {
// Optimistic updates
console.log('Creating user:', vars);
},
success: (response) => {
// Invalidate and refresh users list
QueryCache.refresh(['users']);
},
failure: (error) => {
// Handle error, maybe show toast
console.error('Failed to create user:', error.message);
}
}
);
// Usage in component
let name = $state('');
let email = $state('');
async function handleSubmit() {
const result = await createUser.submit({ name, email });
if (result) {
// Success! Clear form
name = '';
email = '';
}
}Advanced Cache Management
// Get cached data without triggering fetch
const cachedPosts = QueryCache.get(['posts']);
// Refresh specific query in background
QueryCache.refresh(['posts', 'popular']);
// Invalidate related queries
QueryCache.invalidate(['posts'], { bulk: true }); // All post queries
QueryCache.invalidate(['user', userId]); // Specific user
QueryCache.invalidate(); // Everything🎁 Advantages
- Zero external dependencies
- Minimal bundle size
- Built specifically for Svelte 5 runes
- Intelligent caching with TTL and background refresh
- Automatic debouncing and duplicate request prevention
- Flexible Response handling (fetch Response or direct data)
- Type-safe with full TypeScript support
- Dev-friendly with cache debugging in development
📚 API Reference
| Function | Signature | Description |
| ------------------------- | --------------------------------------------------------------- | -------------------------- |
| QueryCache.config(opts) | (opts: { ttl?: number, refresh?: number, enabled?: boolean }) | Set global defaults |
| query | (key: any[], fn: () => Promise<T>, opts?: QueryOptions) | Create reactive query |
| mutation | (fn: (vars: V) => Promise<T>, hooks?: MutationHooks) | Create reactive mutation |
| QueryCache.get | (key: any[]) => T \| undefined | Get cached data |
| QueryCache.refresh | (key: any[]) | Trigger background refetch |
| QueryCache.invalidate | (key?: any[], flag?: { bulk?: boolean }) | Remove cache entries |
QueryOptions
type QueryOptions = {
enabled?: boolean; // Execute immediately (default: true)
refresh?: number; // Background refresh interval in minutes (default: 5)
ttl?: number; // Cache lifetime in minutes (default: 10)
};MutationHooks
type MutationHooks<T, V> = {
processing?: (vars?: V) => void; // Before request
success?: (response: Response) => void; // On success
failure?: (error: { message: string }) => void; // On error
completion?: (response: Response) => void; // Always runs
};✅ When to Use This Package
- Building Svelte 5 applications with data fetching needs
- Want automatic caching without complexity
- Need reactive queries that work seamlessly with runes
- Prefer lightweight solutions over heavy query libraries
- Value clean, maintainable code with minimal boilerplate
🐛 Development Features
In development mode, the cache is exposed on window.QUERY_CACHE for easy debugging and inspection.
✨ Contribute & Feedback
Contributions welcome! Feel free to open issues for bugs, feature requests, or submit PRs to improve the library.
