-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Define 'execution' as in 'before execution begins' #1174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,9 +18,9 @@ A GraphQL service generates a response from a request via execution. | |
- {extensions} (optional): A map reserved for implementation-specific additional | ||
information. | ||
|
||
Given this information, the result of {ExecuteRequest(schema, document, | ||
operationName, variableValues, initialValue)} produces the response, to be | ||
formatted according to the Response section below. | ||
Given this information, the result of {Request(schema, document, operationName, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets think of how we can keep the algorithm names with action verbs. Ideally this has an "execute" or "process" or something here. Considering the prose above describes this as "request via execution" then "execute request" does seem like what a reader would expect |
||
variableValues, initialValue)} produces the response, to be formatted according | ||
to the Response section below. | ||
|
||
Implementations should not add additional properties to a _request_, which may | ||
conflict with future editions of the GraphQL specification. Instead, | ||
|
@@ -39,27 +39,44 @@ and have no effect on the observable execution, validation, or response of a | |
GraphQL document. Descriptions and comments on executable documents MAY be used | ||
for non-observable purposes, such as logging and other developer tools. | ||
|
||
## Executing Requests | ||
## Processing Requests | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the intermixing of "execute" and "process" is ultimately more confusing than helpful, despite it being technically more accurate. I think it's the result of the topline section being "Execution" and then within we define execution as a sub-step of "Processing". It's a much larger change to retitle the entire section. I wonder if instead we could define phases of execution? |
||
|
||
To execute a request, the executor must have a parsed {Document} and a selected | ||
<a name="#sec-Executing-Requests"> | ||
<!-- Legacy link, this section was previously titled "Executing Requests" --> | ||
</a> | ||
|
||
To process a request, the executor must have a parsed {Document} and a selected | ||
operation name to run if the document defines multiple operations, otherwise the | ||
document is expected to only contain a single operation. The result of the | ||
request is determined by the result of executing this operation according to the | ||
"Executing Operations” section below. | ||
request is determined by the result of performing this operation according to | ||
the "Performing Operations” section below. | ||
|
||
The {Request()} algorithm contains the preamble for _execution_, handling | ||
concerns such as determining the operation and coercing the inputs, before | ||
passing the request on to the relevant algorithm for the operation's type which | ||
then performs any other necessary preliminary steps (for example establishing | ||
the source event stream for subscription operations) and then initiates | ||
_execution_. | ||
|
||
Note: An error raised before _execution_ begins will typically be a _request | ||
error_, and once _execution_ begins will typically be an _execution error_. | ||
|
||
:: We define _execution_ as the process of executing the operation's _root | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think here is where we could define "during execution" with something more specific to avoid the overloaded term. These names might not be ideal, but it seems like we have two phases to execution.
I appreciate what we're trying to do here is more clearly define what scenarios lead to an error result with no data entry vs one with a partial data entry. We started with "field error" -- which while unclear for other reasons -- at least evaded this issue. They were clearly errors which occurred at a per-field level. Maybe we just need to do a half-step back to that? |
||
selection set_ through {ExecuteRootSelectionSet()}, and hence _execution_ begins | ||
when {ExecuteRootSelectionSet()} is called for the first time in a request. | ||
|
||
ExecuteRequest(schema, document, operationName, variableValues, initialValue): | ||
Request(schema, document, operationName, variableValues, initialValue): | ||
|
||
- Let {operation} be the result of {GetOperation(document, operationName)}. | ||
- Let {coercedVariableValues} be the result of {CoerceVariableValues(schema, | ||
operation, variableValues)}. | ||
- If {operation} is a query operation: | ||
- Return {ExecuteQuery(operation, schema, coercedVariableValues, | ||
initialValue)}. | ||
- Return {Query(operation, schema, coercedVariableValues, initialValue)}. | ||
- Otherwise if {operation} is a mutation operation: | ||
- Return {ExecuteMutation(operation, schema, coercedVariableValues, | ||
initialValue)}. | ||
- Return {Mutation(operation, schema, coercedVariableValues, initialValue)}. | ||
- Otherwise if {operation} is a subscription operation: | ||
- Return {Subscribe(operation, schema, coercedVariableValues, initialValue)}. | ||
- Return {Subscription(operation, schema, coercedVariableValues, | ||
initialValue)}. | ||
|
||
GetOperation(document, operationName): | ||
|
||
|
@@ -74,27 +91,28 @@ GetOperation(document, operationName): | |
|
||
### Validating Requests | ||
|
||
As explained in the Validation section, only requests which pass all validation | ||
rules should be executed. If validation errors are known, they should be | ||
reported in the list of "errors" in the response and the request must fail | ||
without execution. | ||
As explained in the Validation section, only operations from documents which | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice cleanup here |
||
pass all validation rules should be executed. If validation errors are known, | ||
they should be reported in the list of "errors" in the response and the request | ||
must fail without execution. | ||
|
||
Typically validation is performed in the context of a request immediately before | ||
execution, however a GraphQL service may execute a request without immediately | ||
validating it if that exact same request is known to have been validated before. | ||
A GraphQL service should only execute requests which _at some point_ were known | ||
to be free of any validation errors, and have since not changed. | ||
calling {Request()}, however a GraphQL service may process a request without | ||
immediately validating the document if that exact same document is known to have | ||
been validated before. A GraphQL service should only execute operations which | ||
_at some point_ were known to be free of any validation errors, and have since | ||
not changed. | ||
|
||
For example: the request may be validated during development, provided it does | ||
not later change, or a service may validate a request once and memoize the | ||
result to avoid validating the same request again in the future. | ||
For example: the document may be validated during development, provided it does | ||
not later change, or a service may validate a document once and memoize the | ||
result to avoid validating the same document again in the future. | ||
|
||
### Coercing Variable Values | ||
|
||
If the operation has defined any variables, then the values for those variables | ||
need to be coerced using the input coercion rules of variable's declared type. | ||
If a _request error_ is encountered during input coercion of variable values, | ||
then the operation fails without execution. | ||
then the request fails without _execution_. | ||
|
||
CoerceVariableValues(schema, operation, variableValues): | ||
|
||
|
@@ -131,7 +149,11 @@ CoerceVariableValues(schema, operation, variableValues): | |
|
||
Note: This algorithm is very similar to {CoerceArgumentValues()}. | ||
|
||
## Executing Operations | ||
## Performing Operations | ||
|
||
<a name="#sec-Executing-Operations"> | ||
<!-- Legacy link, this section was previously titled "Executing Operations" --> | ||
</a> | ||
|
||
The type system, as described in the "Type System" section of the spec, must | ||
provide a query root operation type. If mutations or subscriptions are | ||
|
@@ -144,9 +166,9 @@ If the operation is a query, the result of the operation is the result of | |
executing the operation’s _root selection set_ with the query root operation | ||
type. | ||
|
||
An initial value may be provided when executing a query operation. | ||
An initial value may be provided when performing a query operation. | ||
|
||
ExecuteQuery(query, schema, variableValues, initialValue): | ||
Query(query, schema, variableValues, initialValue): | ||
|
||
- Let {queryType} be the root Query type in {schema}. | ||
- Assert: {queryType} is an Object type. | ||
|
@@ -164,7 +186,7 @@ It is expected that the top level fields in a mutation operation perform | |
side-effects on the underlying data system. Serial execution of the provided | ||
mutations ensures against race conditions during these side-effects. | ||
|
||
ExecuteMutation(mutation, schema, variableValues, initialValue): | ||
Mutation(mutation, schema, variableValues, initialValue): | ||
|
||
- Let {mutationType} be the root Mutation type in {schema}. | ||
- Assert: {mutationType} is an Object type. | ||
|
@@ -176,12 +198,13 @@ ExecuteMutation(mutation, schema, variableValues, initialValue): | |
|
||
If the operation is a subscription, the result is an _event stream_ called the | ||
_response stream_ where each event in the event stream is the result of | ||
executing the operation for each new event on an underlying _source stream_. | ||
executing the operation’s _root selection set_ for each new event on an | ||
underlying _source stream_. | ||
|
||
Executing a subscription operation creates a persistent function on the service | ||
Performing a subscription operation creates a persistent function on the service | ||
that maps an underlying _source stream_ to a returned _response stream_. | ||
|
||
Subscribe(subscription, schema, variableValues, initialValue): | ||
Subscription(subscription, schema, variableValues, initialValue): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one is admittedly the weirdest. Regardless of how we net-out on naming, we should rethink the terms used for the phases of subscription |
||
|
||
- Let {sourceStream} be the result of running | ||
{CreateSourceEventStream(subscription, schema, variableValues, initialValue)}. | ||
|
@@ -190,9 +213,9 @@ Subscribe(subscription, schema, variableValues, initialValue): | |
variableValues)}. | ||
- Return {responseStream}. | ||
|
||
Note: In a large-scale subscription system, the {Subscribe()} and | ||
{ExecuteSubscriptionEvent()} algorithms may be run on separate services to | ||
maintain predictable scaling properties. See the section below on Supporting | ||
Note: In a large-scale subscription system, the {Subscription()} and | ||
{SubscriptionEvent()} algorithms may be run on separate services to maintain | ||
predictable scaling properties. See the section below on Supporting | ||
Subscriptions at Scale. | ||
|
||
As an example, consider a chat application. To subscribe to new messages posted | ||
|
@@ -313,8 +336,7 @@ MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues): | |
- Let {responseStream} be a new _event stream_. | ||
- When {sourceStream} emits {sourceValue}: | ||
- Let {executionResult} be the result of running | ||
{ExecuteSubscriptionEvent(subscription, schema, variableValues, | ||
sourceValue)}. | ||
{SubscriptionEvent(subscription, schema, variableValues, sourceValue)}. | ||
- If internal {error} was raised: | ||
- Cancel {sourceStream}. | ||
- Complete {responseStream} with {error}. | ||
|
@@ -328,21 +350,21 @@ MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues): | |
- Complete {responseStream} normally. | ||
- Return {responseStream}. | ||
|
||
Note: Since {ExecuteSubscriptionEvent()} handles all _execution error_, and | ||
_request error_ only occur during {CreateSourceEventStream()}, the only | ||
remaining error condition handled from {ExecuteSubscriptionEvent()} are internal | ||
exceptional errors not described by this specification. | ||
Note: Since {SubscriptionEvent()} handles all _execution error_, and _request | ||
error_ only occur during {CreateSourceEventStream()}, the only remaining error | ||
condition handled from {SubscriptionEvent()} are internal exceptional errors not | ||
described by this specification. | ||
|
||
ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue): | ||
SubscriptionEvent(subscription, schema, variableValues, initialValue): | ||
|
||
- Let {subscriptionType} be the root Subscription type in {schema}. | ||
- Assert: {subscriptionType} is an Object type. | ||
- Let {rootSelectionSet} be the _root selection set_ in {subscription}. | ||
- Return {ExecuteRootSelectionSet(variableValues, initialValue, | ||
subscriptionType, rootSelectionSet, "normal")}. | ||
|
||
Note: The {ExecuteSubscriptionEvent()} algorithm is intentionally similar to | ||
{ExecuteQuery()} since this is how each event result is produced. | ||
Note: The {SubscriptionEvent()} algorithm is intentionally similar to {Query()} | ||
since this is how each event result is produced. | ||
|
||
#### Unsubscribe | ||
|
||
|
@@ -638,7 +660,7 @@ A valid GraphQL executor can resolve the four fields in whatever order it chose | |
(however of course `birthday` must be resolved before `month`, and `address` | ||
before `street`). | ||
|
||
When executing a mutation, the selections in the top most selection set will be | ||
When performing a mutation, the selections in the top most selection set will be | ||
executed in serial order, starting with the first appearing field textually. | ||
|
||
When executing a collected fields map serially, the executor must consider each | ||
|
@@ -788,9 +810,9 @@ CoerceArgumentValues(objectType, field, variableValues): | |
Any _request error_ raised as a result of input coercion during | ||
{CoerceArgumentValues()} should be treated instead as an _execution error_. | ||
|
||
Note: Variable values are not coerced because they are expected to be coerced | ||
before executing the operation in {CoerceVariableValues()}, and valid operations | ||
must only allow usage of variables of appropriate types. | ||
Note: Variable values are not coerced because they are expected to be coerced by | ||
{CoerceVariableValues()} before _execution_ begins, and valid operations must | ||
only allow usage of variables of appropriate types. | ||
|
||
Note: Implementations are encouraged to optimize the coercion of an argument's | ||
default value by doing so only once and caching the resulting coerced value. | ||
|
Uh oh!
There was an error while loading. Please reload this page.