Skip to content

QueryClient

The QueryClient is the core controller of Fasq. It manages the query cache, deduplication, and garbage collection.

It is best to provide the QueryClient at the top of your widget tree using QueryClientProvider, but it can also be used as a singleton if desired.

import 'package:fasq/fasq.dart';
void main() {
final client = QueryClient();
runApp(
QueryClientProvider(
client: client,
child: MyApp(),
),
);
}

You can define default behaviors for all queries in your app.

final client = QueryClient(
config: const CacheConfig(
defaultStaleTime: Duration(minutes: 5), // Data is fresh for 5 mins
defaultCacheTime: Duration(minutes: 30), // Inactive data kept for 30 mins
),
);

Mark data as stale so it gets refetched the next time it’s used.

// Invalidate a single query
client.invalidateQuery('todos'.toQueryKey());
// Invalidate with refetch (default behavior for active queries)
client.invalidateQuery('todos'.toQueryKey(), refetchType: InvalidateType.active);

Load data before the user navigates there to provide an instant experience.

await client.prefetchQuery(
'todos'.toQueryKey(),
() => fetchTodos(),
);

You can update the cache directly without hitting the network (useful for optimistic updates).

// Update data
client.setQueryData<List<Todo>>(
'todos'.toQueryKey(),
(oldData) => [...?oldData, newTodo],
);
// Read data
final todos = client.getQueryData<List<Todo>>('todos'.toQueryKey());

QueryClient supports observers that can monitor all query and mutation lifecycle events. This is useful for logging, analytics, error tracking, and more.

final client = QueryClient();
// Add built-in logger
client.addObserver(FasqLogger());
// Add custom observer
client.addObserver(MyCustomObserver());

Fasq includes FasqLogger for development debugging:

client.addObserver(FasqLogger(
enabled: true,
showData: false, // Privacy-first: don't log data by default
truncateLength: 100,
));

For complete logging documentation, see Logging.

Create custom observers by implementing QueryClientObserver:

class AnalyticsObserver implements QueryClientObserver {
@override
void onQuerySuccess(QuerySnapshot snapshot, QueryMeta? meta, BuildContext? context) {
analytics.track('query_success', {
'key': snapshot.queryKey.toString(),
'duration': calculateDuration(snapshot),
});
}
// Implement other observer methods...
}
// Add observer
client.addObserver(myObserver);
// Remove observer
client.removeObserver(myObserver);
// Clear all observers
client.clearObservers();

QueryClient supports error reporters that receive detailed context when queries fail. This is useful for integrating with external error tracking services like Sentry or Crashlytics.

final client = QueryClient();
// Add error reporter
client.addErrorReporter(SentryErrorReporter());
// You can add multiple reporters
client.addErrorReporter(CrashlyticsErrorReporter());
client.addErrorReporter(CustomErrorReporter());

Implement the FasqErrorReporter interface to create custom reporters:

class SentryErrorReporter implements FasqErrorReporter {
@override
void report(FasqErrorContext context) {
Sentry.captureException(
context.error,
stackTrace: context.stackTrace,
hint: Hint.withMap({
'queryKey': context.queryKey.join('/'),
'retryCount': context.retryCount,
'networkStatus': context.networkStatus ? 'online' : 'offline',
}),
);
}
}

When a query fails, reporters receive a FasqErrorContext containing:

  • Query key that failed
  • Retry count
  • Network status
  • Error and stack trace
  • Sanitized query options (PII automatically removed)
// Add reporter
client.addErrorReporter(myReporter);
// Remove reporter
client.removeErrorReporter(myReporter);

For complete documentation on error tracking, including PII sanitization, integration examples, and best practices, see Error Tracking.

Fasq provides built-in performance monitoring to help you understand your app’s query behavior and optimize performance.

Get a comprehensive snapshot of all performance metrics:

final client = QueryClient();
final snapshot = client.getMetrics();
print('Cache hit rate: ${snapshot.cacheMetrics.hitRate}');
print('Active queries: ${snapshot.activeQueries}');
print('Memory usage: ${snapshot.memoryUsageBytes / 1024 / 1024} MB');

Get detailed metrics for a specific query:

final metrics = client.getQueryMetrics('todos'.toQueryKey());
if (metrics != null) {
print('Fetch count: ${metrics.fetchCount}');
print('Avg fetch time: ${metrics.averageFetchTime?.inMilliseconds}ms');
print('Throughput: ${metrics.throughputMetrics?.requestsPerMinute} RPM');
}

Export metrics to external monitoring systems (console, JSON, OpenTelemetry):

final client = QueryClient();
client.configureMetricsExporters(
MetricsConfig(
exporters: [
ConsoleExporter(),
JsonExporter(),
],
exportInterval: Duration(minutes: 1),
enableAutoExport: true,
),
);

For more details, see the Performance Monitoring and Metrics Exporters documentation.

MethodDescription
fetchQueryFetches a query and returns the result (throws on error).
prefetchQueryFetches a query and stores it in cache (no return, safe).
getQueryDataSynchronously returns the current data in cache.
setQueryDataSynchronously updates the data in cache.
invalidateQueryMarks queries as stale and potentially refetches them.
cancelQueryCancels any outgoing fetches for a query.
resetQueryResets a query to its initial state.
addObserverAdds a QueryClientObserver to monitor query/mutation events.
removeObserverRemoves an observer from the client.
clearObserversRemoves all observers from the client.
addErrorReporterAdds an error reporter to receive query failure notifications.
removeErrorReporterRemoves an error reporter from the client.
getMetricsReturns a snapshot of global performance metrics.
getQueryMetricsReturns performance metrics for a specific query.
configureMetricsExportersConfigures metrics exporters and auto-export.
exportMetricsManuallyManually triggers an immediate metrics export.