In a Flutter app using graphql_flutter, how do you build a GraphQLClient and make it available to Query and Mutation widgets?
answer
- links chained, cache attached
- AuthLink(getToken:).concat(HttpLink)
- GraphQLCache(store: HiveStore())
- await initHiveForFlutter() in main
- ValueNotifier into GraphQLProvider
basics
~10 sChain an AuthLink onto an HttpLink, give GraphQLClient that link and a GraphQLCache (HiveStore for persistence, after await initHiveForFlutter()), wrap it in a ValueNotifier and pass it to a GraphQLProvider above MaterialApp.
solid answer
~40 sA `GraphQLClient` needs two things: a `link` that sends operations and a `cache`. For the link I build `HttpLink('https://api.example.com/graphql')` and put `AuthLink(getToken: () async => 'Bearer $token')` in front with `authLink.concat(httpLink)`; `getToken` runs for every request and must return the full header value. For the cache, `GraphQLCache()` defaults to an in-memory store; to persist it I call `await initHiveForFlutter()` in `main` and pass `GraphQLCache(store: HiveStore())`. The client goes into a `ValueNotifier<GraphQLClient>` handed to `GraphQLProvider`, which I place above `MaterialApp` so every `Query`, `Mutation` and `Subscription` widget, and the hooks, find it through the context. Replacing `notifier.value`, for example after logout, rebuilds the widgets that depend on it.
code
dart · 23 linesimport 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
Future<void> main() async {
await initHiveForFlutter();
final httpLink = HttpLink('https://api.bookclub.example/graphql');
final authLink = AuthLink(getToken: () async => 'Bearer ${await tokenStore.read()}');
final client = ValueNotifier(
GraphQLClient(
link: authLink.concat(httpLink),
cache: GraphQLCache(store: HiveStore()),
),
);
runApp(
GraphQLProvider(
client: client,
child: const MaterialApp(home: ReadingListScreen()),
),
);
}go deeper
Know the four objects: HttpLink, AuthLink, GraphQLCache and GraphQLClient, and that GraphQLProvider above MaterialApp makes the client available.
Explain link composition, why getToken runs per request, the in-memory default store versus HiveStore, and why the provider takes a ValueNotifier.
Plan how the client is replaced on logout, how the persisted cache is cleared, and where subscriptions split off to a WebSocketLink.
Decide whether a GraphQL client library, plain HTTP with typed models, or code generation best fits the team and the backend contract.
## The pieces `graphql_flutter` is the Flutter layer over the standalone `graphql` package. Setting it up means building three objects and one widget: | Piece | Class | Job | |---|---|---| | transport | `HttpLink` | sends queries and mutations as HTTP requests | | request middleware | `AuthLink` | adds an auth header to each request | | cache | `GraphQLCache` with a `Store` | normalises and keeps results | | client | `GraphQLClient(link:, cache:)` | runs operations, applies policies | | provider | `GraphQLProvider(client: ValueNotifier<GraphQLClient>)` | exposes the client to the widget tree | ## Links A **link** is one step in the pipeline an operation passes through. Links are composed, and the last one must actually send the request (a **terminating link**): - **`HttpLink(uri)`** is the usual terminating link for queries and mutations. - **`AuthLink(getToken: ...)`** is a middleware link. `getToken` is called **for every request**, can be `async`, and must return the **complete header value** including any `Bearer ` prefix; the header name defaults to `Authorization` and can be changed with `headerKey`. - **`authLink.concat(httpLink)`** chains them: auth first, then HTTP. - For subscriptions, a `WebSocketLink` is added with **`Link.split((request) => request.isSubscription, wsLink, httpChain)`**; without the split, subscription operations go to the HTTP link. Because `getToken` runs per request, it can read the current token from secure storage or an auth service; the client does not need rebuilding when a token is refreshed. ## The cache and persistence `GraphQLCache` stores results **normalised** by type and id, so two queries that return the same book share one record. Its `store` decides where that data lives: 1. **Default: `InMemoryStore`**, lost when the app process ends. 2. **`HiveStore`**, backed by Hive boxes on disk, so the last results are available on the next cold start. `HiveStore` needs Hive to be initialised first. In a Flutter app you call **`await initHiveForFlutter()`** in `main` before `runApp`: it ensures the widgets binding exists, points Hive at the app documents directory (on non-web platforms), and opens the default box. Forgetting it is the classic setup failure: the store cannot open its box. ## The provider `GraphQLProvider` takes a **`ValueNotifier<GraphQLClient>`**, not a bare client. It listens to the notifier and exposes the client through an inherited widget, so: - `Query`, `Mutation` and `Subscription` widgets, and the `useQuery`/`useMutation`/`useSubscription` hooks, find the client from their `BuildContext`; - assigning a new client to `notifier.value` (after logout, or when switching environments) rebuilds the dependents with the new client; - `GraphQLProvider.of(context)` returns the notifier for code that needs the client directly, such as a repository calling `client.query`. The package recommends placing the provider **above `MaterialApp`**, so every route pushed by the navigator is inside it. ## Logging out and clearing the cache A persisted cache outlives the session that filled it, so logout needs care, especially on a shared device: - **clear the data**: `client.resetStore(refetchQueries: false)` resets the store (for `HiveStore` that clears its box); the method is marked experimental in the package; - **drop the client**: assign a fresh `GraphQLClient` to the `ValueNotifier` so no widget keeps watching queries that belonged to the previous member; - **stop the token**: make `getToken` return `null` once signed out; `AuthLink` then adds no header rather than an empty one. Skipping these steps is how one member's reading list flashes up for the next person who signs in. ## A book-club app, concretely For a book-club app the client is built once in `main`: the HTTP endpoint for queries and mutations on reviews, an `AuthLink` reading the member's token, a `HiveStore` so the reading list shows instantly on launch, and a `GraphQLProvider` wrapping the whole app. Screens then declare what they need with `Query` widgets or hooks, and never construct a client themselves.
- Why does GraphQLProvider take a ValueNotifier rather than a GraphQLClient?So the client can be swapped at runtime. The provider listens to the notifier and rebuilds its inherited widget when `value` changes, so after logout you can assign a fresh client with an empty cache and every `Query` and `Mutation` below picks it up.
- What does GraphQLCache use when you pass no store?An `InMemoryStore`. Results are normalised and shared across queries while the app runs but are gone after a restart. Pass `HiveStore()`, after `initHiveForFlutter()`, to keep them on disk.
saying these in an interview costs you the question
- GraphQLCache persists to disk by default
- AuthLink adds the Bearer prefix for you
- getToken is called once when the client is created
- GraphQLProvider can be placed anywhere, even below the screens that query
- HiveStore works without initialising Hive first