43 lines
2.3 KiB
Markdown
43 lines
2.3 KiB
Markdown
|
# async-graphql Architecture
|
||
|
|
||
|
This document describes the internal architecture of `async-graphql`, and can be useful to
|
||
|
people wanting to contribute.
|
||
|
|
||
|
## Schema
|
||
|
|
||
|
When you create a schema, the first thing it does it asks the query, mutation and subscription types
|
||
|
to register themselves in the schema's list of GraphQL types called the **registry**. Those types
|
||
|
will then recursively register all the types that they depend on in the registry, and so on until
|
||
|
every single type that is used has been registered in the registry.
|
||
|
|
||
|
## Query Execution
|
||
|
|
||
|
First of all, `async-graphql` will use the `async-graphql-parser` crate (located in the `parser/`
|
||
|
directory) to parse the request document source. This also performs some necessary validations
|
||
|
such as making sure that operation (i.e. query/mutation/subscription) names are unique and that the
|
||
|
query does not contain an anonymous operation as well as a named one.
|
||
|
|
||
|
It then will validate the document as per the rest of GraphQL's validation rules. The `validation/`
|
||
|
module handles this, and it works by walking through the entire document once and notifying the
|
||
|
validation rules on the way to perform their validations and report errors if necessary. If
|
||
|
`ValidationMode::Fast` is turned on, far far fewer rules are used.
|
||
|
|
||
|
Also at this stage some non-validators use the same architecture, such as the query depth calculator
|
||
|
which keeps track of how deeply nested the query gets as the document is walked through.
|
||
|
|
||
|
At this point all the unnecessary operations (ones not selected by `operationName`) are dropped, and
|
||
|
we will execute just one.
|
||
|
|
||
|
At the core of all the resolver logic there are two traits: `InputValueType` and `OutputValueType`
|
||
|
which represent a GraphQL input value and GraphQL output value respectively. `InputValueType` just
|
||
|
requires conversions to and from `async_graphql::Value`. `OutputValueType` is an async trait with a
|
||
|
single method, `resolve`, which takes a field (e.g. `user(name: "sunli829") { display_name }`) and
|
||
|
resolves it to a single value.
|
||
|
|
||
|
Scalars and enums are expected to ignore the input and serialize themselves, while objects,
|
||
|
interfaces and unions are expected to read the selection set in the field and resolve and serialize
|
||
|
each one of their fields.
|
||
|
|
||
|
As implementing `OutputValueType::resolve` manually quickly becomes very tedious helpful utilities
|
||
|
are provided in the `resolver_utils` module and via macros.
|