graphql/client.dart is heavily modeled on the Apollo Client.
Many of its newer or "fancier" features we do not have the bandwidth to
support, but it is still commonly our reference point for design for
graphql and other libraries in the ecosystem. Apollo is the source of
our cache design and API, the execution "link" layer, and the type
policies in the normalize package.
tree -L 3 -d .
.
├── example
│ ├── bin
│ └── lib
├── lib
│ └── src
│ ├── cache # cache and store abstractions
│ ├── core #
│ ├── exceptions # client exceptions / link exception wrappers
│ ├── links # custom and re-exported gql_links
│ ├── scheduler # used by ObservableQuery for polling
│ └── utilities # helpers like gql
└── test
└── cacheOf the subpackages in lib/src, most development work is done in the
cache, core, and lib/src/links/websocket_link.
It is also sometimes necessary to contribute to gql_link dependencies
for execution layer issues and normalize for cache normalization
issues, and valuable to understand both.
tree lib/src/core
├── _base_options.dart # shared base class for *Options classes
├── _data_class.dart # stopgap helper for a truly immutable options API
├── _query_write_handling.dart # some common write handling extracted from
│ # the QueryManager.
├── core.dart # reexports.
├── fetch_more.dart # shared fetchMore logic.
├── mutation_options.dart # MutationOptions and MutationCallbackHandler.
├── observable_query.dart # the guts of watchQuery/watchMutation.
├── policies.dart # Policies and default policies.
├── query_manager.dart # The central execution entrypoints for
│ # query, mutate, and subscribe.
├── query_options.dart # QueryOptions, WatchQueryOptions, FetchMoreOptions
└── query_result.dart # QueryResult and MultiSourceResultThe core module contains the vast majority of the main execution path,
with the QueryManager handling the logic for individual requests,
including determining when to do things like read optimistic data, write
to the cache, etc (though the guts of the latter is mostly in
_query_write_handling.dart now).
One confusing aspect of cor is that *Options and QueryResult are not
well integrated with Request and Response from the newer
gql_link system.
Finally, the ObservableQuery is the overpowered workhorse that enables
the streaming design of graphql_flutter. It is also used in Mutation,
which is why it has an onData callback system. The conflation of Query
and Mutation here is not good, and actually lead to a long-standing
bug where mutation results were overwritten that was only resolved in #795.
tree lib/src/cache
├── _normalizing_data_proxy.dart # Operation normalization code shared by
│ # GraphQLCache and OptimisticProxy.
├── _optimistic_transactions.dart # OptimisticProxy for optimistic transactions.
├── cache.dart # Puts it all together and implements "optimistic layers."
├── data_proxy.dart # The abstract GraphQLDataProxy api.
├── fragment.dart # `gql_exec`-like API for readFragment and writeFragment.
├── hive_store.dart # The recommended store using hive.
└── store.dart # The Store abstraction.The GraphQLCache implements a layered optimism system similar to apollo,
allowing for clean separation of the optimistic data from each mutation
that is then merged at read time.
Other than the ability for users to provide their own custom Store, the
rest of the cache normalization magic is handled by the normalize
library.
exceptions defines a few custom LinkExceptions as well as the
OperationException wrapper. The QueryManager wraps all requests in
try/catch and should never throw itself, always wrapping errors in
an OperationException for users to deal with as they see fit.
scheduler is injected into ObservableQuery and is kinda
awkwardly placed. I've only ever had to touch it once or twice.
We re-export a lot of links from gql, and a lot of the code in
gql_http_link came from graphql/client.dart initially.
- #712: Update and switch to
gql_dio_linklink (this is going to be hard). - #798: Decoupling the mutation logic from
ObservableQuery. - #652: Break client features into links and link compositions.