This document describes the internal architecture of async-graphql
, and can be useful to
people wanting to contribute.
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.
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: InputType
and OutputType
which represent a GraphQL input value and GraphQL output value respectively. InputType
just
requires conversions to and from async_graphql::Value
. OutputType
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 OutputType::resolve
manually quickly becomes very tedious helpful utilities
are provided in the resolver_utils
module and via macros.