dartpollo
A Dart GraphQL client with built-in caching support. Executes typed GraphQL queries generated by dartpollo_generator.
Features
- Typed GraphQL execution — Execute generated query classes and get typed responses
- Streaming support — Stream query responses for real-time updates
- Caching — Built-in cache layer with pluggable stores (in-memory, Hive)
- Cache policies — Control caching behavior per-query (
cacheFirst,networkFirst,cacheOnly,networkOnly,cacheAndNetwork) - Configurable TTL — Set time-to-live for cache entries globally or per-request
- Direct cache access — Read, write, evict, and clear cache entries programmatically
- Request cancellation — Cancel in-flight requests using Dio's
CancelToken - Typed error handling — Re-exported DioLink exception types for precise error catching
- GQL link support — Built on top of the
gql_linkecosystem with Dio as the HTTP client
Installation
dependencies:
dartpollo: ^0.1.0-alpha.2
dev_dependencies:
build_runner: ^2.10.0
dartpollo_generator: ^0.1.0-alpha.1
Usage
Basic Client
import 'package:dartpollo/dartpollo.dart';
final client = DartpolloClient('https://api.example.com/graphql');
final response = await client.execute(MyQuery());
print(response.data);
// Don't forget to dispose when done
client.dispose();
Default Headers
final client = DartpolloClient(
'https://api.example.com/graphql',
defaultHeaders: {'Authorization': 'Bearer token'},
);
Custom Dio Instance
import 'package:dio/dio.dart';
final dio = Dio()
..options.headers['Authorization'] = 'Bearer token';
final client = DartpolloClient(
'https://api.example.com/graphql',
client: dio,
);
GET Requests for Queries
// Useful for CDN caching
final client = DartpolloClient(
'https://api.example.com/graphql',
useGETForQueries: true,
);
Request Cancellation
final cancelToken = CancelToken();
final response = await client.execute(
MyQuery(),
context: Context().withCancelToken(cancelToken),
);
// Cancel the request (e.g., on widget dispose)
cancelToken.cancel('Widget disposed');
Error Handling
try {
final response = await client.execute(MyQuery());
} on DioLinkServerException catch (e) {
print('Server error: ${e.statusCode}');
} on DioLinkTimeoutException {
print('Request timed out');
} on DioLinkCanceledException {
print('Request was cancelled');
} on DioLinkParserException {
print('Failed to parse response');
}
From Custom Link
final client = DartpolloClient.fromLink(
Link.from([
DedupeLink(),
DioLink('https://api.example.com/graphql'),
]),
);
Cached Client
final client = DartpolloCachedClient(
'https://api.example.com/graphql',
cacheStore: InMemoryCacheStore(maxSize: 100),
defaultCachePolicy: CachePolicy.cacheFirst,
defaultCacheTtl: Duration(minutes: 5),
);
// Execute with default cache policy
final response = await client.execute(MyQuery());
// Override cache policy per-request
final freshResponse = await client.execute(
MyQuery(),
context: Context().withCachePolicy(CachePolicy.networkOnly),
);
// Override both policy and TTL
final customResponse = await client.execute(
MyQuery(),
context: Context().withCache(
policy: CachePolicy.cacheFirst,
ttl: Duration(hours: 1),
),
);
Streaming Responses
// Useful with cacheAndNetwork to get cached data first, then fresh data
client.stream(
MyQuery(),
context: Context().cacheAndNetwork(),
).listen((response) {
// First emission: cached data (if available)
// Second emission: fresh network data
print(response.data);
});
Direct Cache Management
// Read from cache
final cached = await client.readCache(MyQuery());
// Write to cache (e.g., optimistic updates)
await client.writeCache(
MyQuery(),
{'user': {'id': '123', 'name': 'John Doe'}},
ttl: Duration(minutes: 10),
);
// Evict a specific query from cache
await client.evictCache(MyQuery());
// Clear all cache
await client.clearCache();
// Get cache statistics
final stats = client.getCacheStats();
print('Cache size: ${stats['size']}');
API
Clients
DartpolloClient(String graphQLEndpoint, {Dio? client, Map<String, String> defaultHeaders, bool useGETForQueries, bool serializableErrors})— Standard GraphQL client with DioDartpolloClient.fromLink(Link link)— Client from a custom link chainDartpolloCachedClient(String graphQLEndpoint, {Dio? client, CacheStore? cacheStore, CachePolicy defaultCachePolicy, Duration? defaultCacheTtl, Map<String, String> defaultHeaders, bool useGETForQueries, bool serializableErrors})— Client with caching supportDartpolloCachedClient.fromLink(Link link, {required CacheLink cacheLink, DioLink? dioLink})— Cached client from a custom link chain
Client Methods
| Method | Available On | Description |
|---|---|---|
execute(query, {context}) |
Both | Execute a query and return a typed response |
stream(query, {context}) |
Both | Stream typed responses (useful with cacheAndNetwork) |
readCache(query) |
DartpolloCachedClient |
Read cached data for a query |
writeCache(query, data, {ttl}) |
DartpolloCachedClient |
Write data to cache |
evictCache(query) |
DartpolloCachedClient |
Remove a query from cache |
clearCache() |
DartpolloCachedClient |
Clear all cached data |
getCacheStats() |
DartpolloCachedClient |
Get cache size and configuration info |
dispose() |
Both | Close the Dio client and release resources |
Cache
CacheStore— Abstract cache store interfaceInMemoryCacheStore— In-memory cache implementation with optional max sizeHiveCacheStore— Persistent cache using HiveCacheLink— GQL link that intercepts requests for cachingCachePolicy— Cache behavior policies (cacheFirst,networkFirst,cacheOnly,networkOnly,cacheAndNetwork)CacheEntry— Individual cache entries with data, timestamp, and TTLCacheContext— Context entries and extensions for per-request cache configuration
Context Extensions
The CacheContextExtension on Context provides a fluent API for per-request cache configuration:
| Extension | Description |
|---|---|
withCachePolicy(CachePolicy) |
Override cache policy |
withCacheTtl(Duration?) |
Override cache TTL |
withCache({policy, ttl}) |
Override both policy and TTL |
withoutCache() |
Shorthand for networkOnly |
cacheOnly() |
Shorthand for cacheOnly |
cacheAndNetwork() |
Shorthand for cacheAndNetwork |
withCancelToken(CancelToken) |
Attach a Dio CancelToken to cancel the request |
DioLink Exceptions
The following exception types are re-exported from gql_dio_link for typed error handling:
| Exception | When |
|---|---|
DioLinkServerException |
HTTP status >= 300 or missing data+errors |
DioLinkTimeoutException |
Connect/send/receive timeout |
DioLinkCanceledException |
Request cancelled via CancelToken |
DioLinkParserException |
Response parsing failed |
DioLinkUnkownException |
Any other Dio error |
Migration Guide
If you were using the old monolithic dartpollo package (before the monorepo split), follow these steps to migrate:
1. Update pubspec.yaml
The code generator has been extracted into a separate package. Add dartpollo_generator as a dev dependency:
Before:
dependencies:
dartpollo:
# your version/path
dev_dependencies:
build_runner: ^2.10.0
After:
dependencies:
dartpollo: ^0.1.0-alpha.2
dev_dependencies:
build_runner: ^2.10.0
dartpollo_generator: ^0.1.0-alpha.1 # ← NEW: generator is now a separate package
Note:
dartpollo_annotationis automatically included as a transitive dependency — you don't need to add it manually.
2. Update build.yaml
Two changes are needed:
- Builder key changed from
dartpollo:todartpollo_generator|dartpollo:(fully qualifiedpackage|builder_nameformat) outputoption removed — output paths are now auto-generated under__generated__/directories. Remove anyoutput:entries from yourschema_mapping.
Before:
targets:
$default:
sources:
- $package$
- lib/**
- schema.graphql
builders:
dartpollo:
options:
schema_mapping:
- schema: schema.graphql
queries_glob: lib/**/*.graphql
output: lib/__generated__
After:
targets:
$default:
sources:
- $package$
- lib/**
- schema.graphql
builders:
dartpollo_generator|dartpollo: # ← updated builder key
options:
schema_mapping:
- schema: schema.graphql
queries_glob: lib/**/*.graphql
# output is no longer needed — auto-generated under __generated__/
For the full list of configuration options, see the dartpollo_generator README.
3. Regenerate code
dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs
4. No import changes needed
All existing imports (package:dartpollo/dartpollo.dart) continue to work. The client barrel file re-exports all types from dartpollo_annotation, so generated code and your application code remain compatible without any import changes.
Related Packages
- dartpollo_generator — Code generator for GraphQL schemas
- dartpollo_annotation — Shared types (transitive dependency)