Skip to content

Commit

Permalink
Spec edits to reduce "query ambiguity" (#777)
Browse files Browse the repository at this point in the history
* Spec edits to reduce "query ambiguity"

* Editorialize change sites

I went through this editorial in two passes. One where I tuned the usage of "operation", "request", and "selection set". I hopefully made these spots slightly clearer, but I'm open to feedback. Second pass was more freeform prose edits in cases where I thought the intended ideas could have been clearer and avoid the query ambiguity in the first place.

Co-authored-by: Lee Byron <lee.byron@robinhood.com>
  • Loading branch information
benjie and leebyron authored Apr 9, 2021
1 parent f474e66 commit 9ec15e3
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 105 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ move forward. See editor Lee Byron talk about

Once a query is written, it should always mean the same thing and return the
same shaped result. Future changes should not change the meaning of existing
schema or queries or in any other way cause an existing compliant GraphQL
schema or requests or in any other way cause an existing compliant GraphQL
service to become non-compliant for prior versions of the spec.

* **Performance is a feature**
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ type Droid implements Character {
We're missing one last piece: an entry point into the type system.

When we define a schema, we define an object type that is the basis for all
queries. The name of this type is `Query` by convention, and it describes
query operations. The name of this type is `Query` by convention, and it describes
our public, top-level API. Our `Query` type for this example will look like
this:

Expand Down
2 changes: 1 addition & 1 deletion spec/Section 1 -- Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Which produces the resulting data (in JSON):
```

GraphQL is not a programming language capable of arbitrary computation, but is
instead a language used to query application services that have
instead a language used to make requests to application services that have
capabilities defined in this specification. GraphQL does not mandate a
particular programming language or storage system for application services that
implement it. Instead, application services take their capabilities and map them
Expand Down
49 changes: 24 additions & 25 deletions spec/Section 2 -- Language.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Clients use the GraphQL query language to make requests to a GraphQL service.
We refer to these request sources as documents. A document may contain
operations (queries, mutations, and subscriptions) as well as fragments, a
common unit of composition allowing for query reuse.
common unit of composition allowing for data requirement reuse.

A GraphQL document is defined as a syntactic grammar where terminal symbols are
tokens (indivisible lexical units). These tokens are defined in a lexical
Expand Down Expand Up @@ -337,8 +337,8 @@ under-fetching data.
}
```

In this query, the `id`, `firstName`, and `lastName` fields form a selection
set. Selection sets may also contain fragment references.
In this query operation, the `id`, `firstName`, and `lastName` fields form a
selection set. Selection sets may also contain fragment references.


## Fields
Expand Down Expand Up @@ -438,7 +438,7 @@ Many arguments can exist for a given field:
Arguments may be provided in any syntactic order and maintain identical
semantic meaning.

These two queries are semantically identical:
These two operations are semantically identical:

```graphql example
{
Expand Down Expand Up @@ -546,7 +546,7 @@ query noFragments {
```

The repeated fields could be extracted into a fragment and composed by
a parent fragment or query.
a parent fragment or operation.

```graphql example
query withFragments {
Expand All @@ -567,8 +567,8 @@ fragment friendFields on User {
}
```

Fragments are consumed by using the spread operator (`...`). All fields selected
by the fragment will be added to the query field selection at the same level
Fragments are consumed by using the spread operator (`...`). All fields
selected by the fragment will be added to the field selection at the same level
as the fragment invocation. This happens through multiple levels of fragment
spreads.

Expand Down Expand Up @@ -597,7 +597,7 @@ fragment standardProfilePic on User {
}
```

The queries `noFragments`, `withFragments`, and `withNestedFragments` all
The operations `noFragments`, `withFragments`, and `withNestedFragments` all
produce the same response object.


Expand All @@ -616,7 +616,7 @@ Fragments can be specified on object types, interfaces, and unions.
Selections within fragments only return values when the concrete type of the object
it is operating on matches the type of the fragment.

For example in this query on the Facebook data model:
For example in this operation using the Facebook data model:

```graphql example
query FragmentTyping {
Expand Down Expand Up @@ -1049,7 +1049,7 @@ literal representation of input objects as "object literals."
Input object fields may be provided in any syntactic order and maintain
identical semantic meaning.

These two queries are semantically identical:
These two operations are semantically identical:

```graphql example
{
Expand Down Expand Up @@ -1096,7 +1096,9 @@ If not defined as constant (for example, in {DefaultValue}), a {Variable} can be
supplied for an input value.

Variables must be defined at the top of an operation and are in scope
throughout the execution of that operation.
throughout the execution of that operation. Values for those variables are
provided to a GraphQL service as part of a request so they may be substituted
in during execution.

In this example, we want to fetch a profile picture size based on the size
of a particular device:
Expand All @@ -1111,10 +1113,8 @@ query getZuckProfile($devicePicSize: Int) {
}
```

Values for those variables are provided to a GraphQL service along with a
request so they may be substituted during execution. If providing JSON for the
variables' values, we could run this query and request profilePic of
size `60` width:
If providing JSON for the variables' values, we could request a `profilePic` of
size `60`:

```json example
{
Expand All @@ -1124,11 +1124,10 @@ size `60` width:

**Variable use within Fragments**

Query variables can be used within fragments. Query variables have global scope
with a given operation, so a variable used within a fragment must be declared
in any top-level operation that transitively consumes that fragment. If
a variable is referenced in a fragment and is included by an operation that does
not define that variable, the operation cannot be executed.
Variables can be used within fragments. Variables have global scope with a given operation, so a variable used within a fragment must be declared in any
top-level operation that transitively consumes that fragment. If a variable is
referenced in a fragment and is included by an operation that does not define
that variable, that operation is invalid (see [All Variable Uses Defined](#sec-All-Variable-Uses-Defined)).


## Type References
Expand All @@ -1146,9 +1145,9 @@ NonNullType :
- NamedType !
- ListType !

GraphQL describes the types of data expected by query variables. Input types
may be lists of another input type, or a non-null variant of any other
input type.
GraphQL describes the types of data expected by arguments and variables.
Input types may be lists of another input type, or a non-null variant of any
other input type.

**Semantics**

Expand Down Expand Up @@ -1188,8 +1187,8 @@ including or skipping a field. Directives provide this by describing additional
Directives have a name along with a list of arguments which may accept values
of any input type.

Directives can be used to describe additional information for types, fields, fragments
and operations.
Directives can be used to describe additional information for types, fields,
fragments and operations.

As future versions of GraphQL adopt new configurable execution capabilities,
they may be exposed via directives. GraphQL services and tools may also provide
Expand Down
69 changes: 34 additions & 35 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Type System

The GraphQL Type system describes the capabilities of a GraphQL service and is
used to determine if a query is valid. The type system also describes the
input types of query variables to determine if values provided at runtime
are valid.
used to determine if a requested operation is valid, to guarantee the type of
response results, and describes the input types of variables to determine if
values provided at request time are valid.

TypeSystemDocument : TypeSystemDefinition+

Expand Down Expand Up @@ -79,7 +79,7 @@ schema {
}
"""
Root type for all your queries
Root type for all your query operations
"""
type Query {
"""
Expand Down Expand Up @@ -191,7 +191,7 @@ When using the type system definition language, a document must include at most
one {`schema`} definition.

In this example, a GraphQL schema is defined with both query and mutation
root types:
root operation types:

```graphql example
schema {
Expand Down Expand Up @@ -424,8 +424,8 @@ raised (input values are validated before execution begins).

GraphQL has different constant literals to represent integer and floating-point
input values, and coercion rules may apply differently depending on which type
of input value is encountered. GraphQL may be parameterized by query variables,
the values of which are often serialized when sent over a transport like HTTP. Since
of input value is encountered. GraphQL may be parameterized by variables, the
values of which are often serialized when sent over a transport like HTTP. Since
some common serializations (ex. JSON) do not discriminate between integer
and floating-point values, they are interpreted as an integer input value if
they have an empty fractional part (ex. `1.0`) and otherwise as floating-point
Expand Down Expand Up @@ -601,14 +601,15 @@ FieldsDefinition : { FieldDefinition+ }

FieldDefinition : Description? Name ArgumentsDefinition? : Type Directives[Const]?

GraphQL queries are hierarchical and composed, describing a tree of information.
While Scalar types describe the leaf values of these hierarchical queries, Objects
describe the intermediate levels.
GraphQL operations are hierarchical and composed, describing a tree of
information. While Scalar types describe the leaf values of these hierarchical
operations, Objects describe the intermediate levels.

GraphQL Objects represent a list of named fields, each of which yield a value of
a specific type. Object values should be serialized as ordered maps, where the
queried field names (or aliases) are the keys and the result of evaluating
the field is the value, ordered by the order in which they appear in the query.
selected field names (or aliases) are the keys and the result of evaluating
the field is the value, ordered by the order in which they appear in
the selection set.

All fields defined within an Object type must not have a name which begins with
{"__"} (two underscores), as this is used exclusively by GraphQL's
Expand Down Expand Up @@ -687,8 +688,8 @@ type Person {
}
```

Valid queries must supply a nested field set for a field that returns
an object, so this query is not valid:
Valid operations must supply a nested field set for any field that returns an
object, so this operation is not valid:

```graphql counter-example
{
Expand Down Expand Up @@ -722,7 +723,7 @@ And will yield the subset of each object type queried:
**Field Ordering**

When querying an Object, the resulting mapping of fields are conceptually
ordered in the same order in which they were encountered during query execution,
ordered in the same order in which they were encountered during execution,
excluding fragments for which the type does not apply and fields or
fragments that are skipped via `@skip` or `@include` directives. This ordering
is correctly produced when using the {CollectFields()} algorithm.
Expand Down Expand Up @@ -920,10 +921,10 @@ type Person {
}
```

GraphQL queries can optionally specify arguments to their fields to provide
Operations can optionally specify arguments to their fields to provide
these arguments.

This example query:
This example operation:

```graphql example
{
Expand All @@ -932,7 +933,7 @@ This example query:
}
```

May yield the result:
May return the result:

```json example
{
Expand All @@ -948,9 +949,9 @@ Object, Interface, or Union type).
### Field Deprecation

Fields in an object may be marked as deprecated as deemed necessary by the
application. It is still legal to query for these fields (to ensure existing
clients are not broken by the change), but the fields should be appropriately
treated in documentation and tooling.
application. It is still legal to include these fields in a selection set
(to ensure existing clients are not broken by the change), but the fields should
be appropriately treated in documentation and tooling.

When using the type system definition language, `@deprecated` directives are
used to indicate that a field is deprecated:
Expand Down Expand Up @@ -1062,7 +1063,7 @@ type Contact {
}
```

This allows us to write a query for a `Contact` that can select the
This allows us to write a selection set for a `Contact` that can select the
common fields.

```graphql example
Expand All @@ -1074,10 +1075,10 @@ common fields.
}
```

When querying for fields on an interface type, only those fields declared on
When selecting fields on an interface type, only those fields declared on
the interface may be queried. In the above example, `entity` returns a
`NamedEntity`, and `name` is defined on `NamedEntity`, so it is valid. However,
the following would not be a valid query:
the following would not be a valid selection set against `Contact`:

```graphql counter-example
{
Expand All @@ -1091,7 +1092,7 @@ the following would not be a valid query:

because `entity` refers to a `NamedEntity`, and `age` is not defined on that
interface. Querying for `age` is only valid when the result of `entity` is a
`Person`; the query can express this using a fragment or an inline fragment:
`Person`; this can be expressed using a fragment or an inline fragment:

```graphql example
{
Expand Down Expand Up @@ -1294,11 +1295,9 @@ type SearchQuery {
}
```

When querying the `firstSearchResult` field of type `SearchQuery`, the
query would ask for all fields inside of a fragment indicating the appropriate
type. If the query wanted the name if the result was a Person, and the height if
it was a photo, the following query is invalid, because the union itself
defines no fields:
In this example, a query operation wants the name if the result was a Person,
and the height if it was a photo. However because a union itself defines no
fields, this could be ambiguous and is invalid.

```graphql counter-example
{
Expand All @@ -1309,7 +1308,7 @@ defines no fields:
}
```

Instead, the query would be:
A valid operation includes typed fragments (in this example, inline fragments):

```graphql example
{
Expand Down Expand Up @@ -1415,7 +1414,7 @@ reasonable coercion is not possible they must raise a field error.
GraphQL has a constant literal to represent enum input values. GraphQL string
literals must not be accepted as an enum input and instead raise a request error.

Query variable transport serializations which have a different representation
Variable transport serializations which have a different representation
for non-string symbolic values (for example, [EDN](https://github.com/edn-format/edn))
should only allow such values as enum input values. Otherwise, for most
transport serializations that do not, strings may be interpreted as the enum
Expand Down Expand Up @@ -1709,9 +1708,9 @@ exclamation mark is used to denote a field that uses a Non-Null type like this:

**Nullable vs. Optional**

Fields are *always* optional within the context of a query, a field may be
omitted and the query is still valid. However fields that return Non-Null types
will never return the value {null} if queried.
Fields are *always* optional within the context of a selection set, a field may
be omitted and the selection set is still valid. However fields that return
Non-Null types will never return the value {null} if queried.

Inputs (such as field arguments), are always optional by default. However a
non-null input type is required. In addition to not accepting the value {null},
Expand Down
Loading

0 comments on commit 9ec15e3

Please sign in to comment.