Request overview
The following deliberately over-simplifies the inner workings of Grafast by focussing on single-payload GraphQL requests rather than those that return streams. The aim is to give you a general feel for how the system works.
For a straightforward GraphQL request (one that does not return a stream), the request cycle looks something like this:
- Perform server middleware (typically auth) - outside the scope of Grafast
- Parse and validate GraphQL request (cached)
- Establish the operation plan
- Execute the execution plan
- Execute the output plan
- Return response to user
Parse and validate GraphQL request
Essentially the same as in a graphql-js system, but caching the parse of the document is essential for Grafast performance (in-memory document needs to be identical for plan to be reused).
Establish the operation plan
When Grafast sees an operation for the first time, it builds an operation
plan (composed of an execution plan and associated output plan). Whilst
building the operation plan, it may have also determined particular constraints
that a future request must satisfy in order to use this same operation plan; for
example if the request contained @skip(if: $variable) then a different
operation plan would be needed depending on whether $variable was true or
false. Constraints are kept as narrow and limited as possible to maximize reuse.
When an operation is seen a future time, Grafast looks for an existing operation plan whose constraints fit the request. If there is one then this operation plan can be executed, otherwise a new operation plan is created (see previous paragraph).
Currently different permutations of @skip/@include arguments will produce
different plans since they may require different work to be undertaken. In
future versions we may manage this in a different way, for example by inhibiting
steps related to excluded selection sets on a per-request basis.
Execute the execution plan
Grafast will populate the relevant system steps in the plan with the variables, context value, root value, etc and will then execute the execution plan, the execution flowing down through the execution plan's step graph, executing each step exactly once (and sometimes in parallel with other steps) until all steps have been executed. Since each step is only executed once per request, the execution must process all of the data in a batch. Thus, it is fed a list of data from each of its dependencies, and it must return a corresponding list of data that its dependents may themselves consume.
Execute the output plan
Once the execution plan has executed to completion, the values gathered are processed via the output plan to produce the output. Typically this is a JSON object, however for an optimization Grafast may optionally output stringified JSON instead without ever building the intermediary JavaScript objects.
@stream/`@deferWhen the operation involves streams (subscription or @stream), the relevant
execution and output plan steps take place for each element of the stream(s).
Similarly, deferred selections inside a list may execute more than once.
Returning response to user
This can be the same as in a graphql-js project, but Grafast also supports
an optimized strategy for stringifying the result should you need to do so
(e.g. if you are serving the request over HTTP). For this reason, we recommend
that you use stringifyPayload rather than JSON.stringify on the results
before sending to the user.
import { stringifyPayload, grafast } from "grafast";
const result = await grafast(/*...*/);
console.log(stringifyPayload(result));