Core Concepts
Understanding the fundamental concepts of Fasq will help you build better applications and avoid common pitfalls.
Queries
A query is a declarative dependency on an asynchronous data source. Queries are tied to a unique QueryKey and can be used to fetch, cache, and synchronize data.
Query Lifecycle
- Idle: Query hasn’t started yet.
- Loading: Initial fetch is in progress.
- Success: Data is available.
- Error: Fetch failed.
Query Keys
Fasq uses QueryKey to identify cache entries. The simplest way to create one is using the .toQueryKey() extension on strings.
// Simple key
'users'.toQueryKey()
// Parameterized key (using string interpolation)
'user:$userId'.toQueryKey()
// Hierarchical key
'posts:user:$userId:page:$page'.toQueryKey()Typed Query Keys
For type safety, you can use TypedQueryKey.
// Strongly typed key for List<User>
const usersKey = TypedQueryKey<List<User>>('users');Mutations
Mutations are for creating, updating, or deleting data. Unlike queries:
- They are active only when triggered (via
mutate()). - They do not auto-refetch.
- They are one-off operations.
MutationBuilder<User, String>(
mutationFn: (name) => api.createUser(name),
builder: (context, state, mutate) {
return ElevatedButton(
onPressed: () => mutate('New Name'),
child: Text('Create'),
);
},
)Caching & Staleness
Fasq implements a Stale-While-Revalidate strategy.
1. Stale Time
The duration data is considered “fresh”.
- If data is fresh, Fasq returns it from cache without a network request.
- If data is stale, Fasq returns it from cache immediately, but triggers a background refetch.
- Default:
Duration.zero(always stale).
2. Cache Time
The duration inactive data remains in memory.
- If a query is unmounted (e.g., user leaves the screen), the data sits in cache for this duration.
- If accessed again within this time, it’s reused.
- If not accessed, it is garbage collected.
- Default:
5 minutes.
QueryOptions(
staleTime: Duration(minutes: 5), // Trust cache for 5 minutes
cacheTime: Duration(minutes: 30), // Keep in memory for 30 minutes
)Request Deduplication
If multiple widgets request the same queryKey at the same time, Fasq sends only one network request. Once the request completes, all widgets update with the data simultaneously.
// Widget A
QueryBuilder(queryKey: 'data'.toQueryKey(), ...)
// Widget B (same key)
QueryBuilder(queryKey: 'data'.toQueryKey(), ...)
// -> 1 Fetch only!Global State
The QueryClient acts as the single source of truth. It holds the cache and manages all active queries.
// Access globally
final client = QueryClient();
// Invalidate specific data
client.invalidateQuery('users'.toQueryKey());
// Prefetch data
client.prefetchQuery('profile'.toQueryKey(), fetchProfile);