Skip to content

Commit

Permalink
Add CoerceResult() (#836)
Browse files Browse the repository at this point in the history
This factors out a step from `CompleteValue()` about  "coercing" a value to a formal sub-algorithm `CoerceResult()` that mirrors the language used in `ResolveAbstractType()` - both refer to "internal methods" provided for each type, which is in fact how the reference implementation applies this coercion behavior.

As editorial, this provides a place to expand on the behavior and purpose as well as link back to the relevant subsection from the type system section.

This also makes a *very important* clarification. The previous step read that if coercion failed then "otherwise null". This actually described a much older version of the reference implementation which did not always produce errors for failed coercion. Years back the reference implementation and the spec changed the "result coercion" subsections of all the built-in scalars to clearly state that field errors are thrown if coercion fails. This step now creates ambiguitity about which behavior is correct - returning null or throwing a field error? In practice both may be correct because of the error handling behavior - but we should be clear that the returned null is a result of that error behavior rather than it being an explicit behavior that's part of `CompleteValue()`. The new pulled out sub-algo makes it clear that the return type must be valid or a field error is produced - which is exactly the behavior described by each built-in scalar.
  • Loading branch information
leebyron committed Apr 8, 2021
1 parent 792671b commit 0062610
Showing 1 changed file with 23 additions and 2 deletions.
25 changes: 23 additions & 2 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,8 +656,7 @@ CompleteValue(fieldType, fields, result, variableValues):
{CompleteValue(innerType, fields, resultItem, variableValues)}, where
{resultItem} is each item in {result}.
* If {fieldType} is a Scalar or Enum type:
* Return the result of "coercing" {result}, ensuring it is a legal value of
{fieldType}, otherwise {null}.
* Return the result of {CoerceResult(fieldType, result)}.
* If {fieldType} is an Object, Interface, or Union type:
* If {fieldType} is an Object type.
* Let {objectType} be {fieldType}.
Expand All @@ -666,6 +665,28 @@ CompleteValue(fieldType, fields, result, variableValues):
* Let {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}.
* Return the result of evaluating {ExecuteSelectionSet(subSelectionSet, objectType, result, variableValues)} *normally* (allowing for parallelization).

**Coercing Results**

The primary purpose of value completion is to ensure that the values returned by
field resolvers are valid according to the GraphQL type system and a service's
schema. This "dynamic type checking" allows GraphQL to provide consistent
guarantees about returned types atop any service's internal runtime.

See the Scalars [Result Coercion and Serialization](#sec-Scalars.Result-Coercion-and-Serialization)
sub-section for more detailed information about how GraphQL's built-in scalars
coerce result values.

CoerceResult(leafType, value):
* Assert {value} is not {null}.
* Return the result of calling the internal method provided by the type
system for determining the "result coercion" of {leafType} given the value
{value}. This internal method must return a valid value for the
type and not {null}. Otherwise throw a field error.

Note: If a field resolver returns {null} then it is handled within
{CompleteValue()} before {CoerceResult()} is called. Therefore both the input
and output of {CoerceResult()} must not be {null}.

**Resolving Abstract Types**

When completing a field with an abstract return type, that is an Interface or
Expand Down

0 comments on commit 0062610

Please sign in to comment.