Skip to content

Commit

Permalink
RFC: Field coordinates
Browse files Browse the repository at this point in the history
  • Loading branch information
magicmark committed Jul 4, 2020
1 parent 478adf3 commit 858a105
Show file tree
Hide file tree
Showing 4 changed files with 377 additions and 0 deletions.
150 changes: 150 additions & 0 deletions rfcs/FieldCoodinateSerialization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# RFC: Field Coordinate Serialization

_This is a sister RFC to [RFC: Field Coordinates](./FieldCoordinates.md). This
document assumes knowledge of the Field Coordinates RFC._

This RFC outlines the seralization of field nodes in a GraphQL query to ["field
coordinates"](./FieldCoordinates.md).

## 📜 Problem Statement

Third party GraphQL tooling and libraries may wish to refer to a field, or set of
fields refered to in a document (e.g. query, mutation or subscription). Use cases include documentation, metrics and logging libraries.

![](https://i.fluffy.cc/g78sJCjCJ0MsbNPhvgPXP46Kh9knBCKF.png)

_(Field coordinates shown in a query - [Apollo Studio](https://www.apollographql.com/docs/studio/)_)

There already exists a convention used by some third party libraries for
calculating field coordinates in a query. However, there is no formal
specification or name for this convention.

This RFC proposes standardization for computing field coordinates.

## 🐕 Worked Example

Consier the following schema:

```graphql
type Person {
name: String
}

type Business {
name: String
owner: String
}

type Query {
searchBusiness(name: String): [Business]
}
```

And the following query:

```graphql
query {
searchBusinesses(name: "El Greco Deli") {
name
owner {
name
}
}
}
```

A GraphQL server may wish to log which fields are accessed in a query, such that
we can compute a list of "most popular fields" in the schema. We may then want to
use this list to optimize the most frequently accessed fields in our schea.

Field coordinates allow us to store this list of "most popular fields".

We want a way to calculate field coordinates from a given field node (and by
extension, calculate a list of all field coordinates contained in a document).

From the query above, we would calculate the following list of field coordinates:

- `Query.searchBusinesses`
- `Business.name`
- `Business.owner`
- `Person.name`

## Examples in industry

- [Apollo Studio](https://www.apollographql.com/docs/studio/) shows field
coodinates when hovering over fields in a query:

![](https://i.fluffy.cc/g78sJCjCJ0MsbNPhvgPXP46Kh9knBCKF.png)

- [extract-field-coordinates](https://github.com/sharkcore/extract-field-coordinates)
extracts a list of field coordinates from a schema and query document.

## Drawbacks

- Requires the corresponding schema as an input to be able to calculate the field
coordinate

- **Edge Cases:** There are some edge cases where we need to decide (or let the
user decide) behaviour:

- What happens when the schema is outdated or a type referenced in the query is
not present in the schema? Do we throw an error as the document/schema is
invalid, or do we try a "best effort" approach to preserve as much information
as possible?

- TODO: add more edge case behaviour from the [sample implementation](https://github.com/sharkcore/extract-field-coordinates)

## Alternatives Considered

### Field Paths

[`path` exists as an attribute on `GraphQLResolveInfo`](https://github.com/graphql/graphql-js/blob/8f3d09b54260565/src/type/definition.js#L951).

Given the following query:

```graphql
query {
searchBusinesses(name: "El Greco Deli") {
name
owner {
name
}
}
}
```

The field coordinate `Person.name` may also be written as the following field
path:

```json
["query", "searchBusinesses", 1, "owner", "name"]
```

- Like field coordinates, this uniquely identifies the "name" field and
disambiguates it from the "name" field on the `Business` type.
- Note that we also didn't need a schema to calculate the field path - this can
be with just the query document.

However - we are unable to tell from a field path alone what the parent _type_
was.

Consider the following query:

```graphql
query {
getUsers(name: "mark") {
name
}
}
```

The same field coordinate `Person.name` in the context of this query would be
written as:

```json
["query", "getUsers", 1, "name"]
```

Field Paths alone are insufficient to do normalization of the field nodes. We
need the parent type information from the schema to work out that both of these
field paths reference the same field in the schema.
122 changes: 122 additions & 0 deletions rfcs/FieldCoordinates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# RFC: Field Coordinates

**Champion:** @magicmark

This RFC proposes formalizing "field coordinates" - a way to uniquely identify a
field defined in a GraphQL Schema.

This may be listed as an appendix item in the official specification to serve as
an official reference to third party library implementations.

## 🚫 What this RFC does _not_ propose

This RFC does not seek to change the GraphQL language in any way.

- There are **no proposed GraphQL runtime changes**
- There are **no proposed changes to how we parse documents**.

## 📜 Problem Statement

Third party GraphQL tooling and libraries may wish to refer to a field, or set of
fields in a schema. Use cases include documentation, metrics and logging
libraries.

![](https://i.fluffy.cc/5Cz9cpwLVsH1FsSF9VPVLwXvwrGpNh7q.png)

_(Example shown from GraphiQL's documentation search tab)_

There already exists a convention used by some third party libraries for writing
out fields in a unique way for such purposes. However, there is no formal
specification or name for this convention.

## ✨ Worked Example

For example, consider the following schema:

```graphql
type Person {
name: String
}

type Business {
name: String
owner: String
}

type Query {
searchBusinesses(name: String): [Business]
}
```

We can write the following list of field coordinates:

- `Person.name` uniquely identifies the "name" field on the "Person" type
- `Business.name` uniquely identifies the "name" field on the "Business"
type
- `Business.owner` uniquely identifies the "owner" field on the "Business" type
- `Query.searchBusinesses` uniquely identifies the "searchBusinesses" field on
the "Query" type

This RFC standardizes how we write field coodinates as above.

## 🎨 Prior art

- The name "field coordinates" comes from [GraphQL Java](https://github.com/graphql-java/graphql-java)
(4.3k stars), where this is already used as described - [Github comment](https://github.com/graphql/graphql-spec/issues/735#issuecomment-646979049) - [Implementation](https://github.com/graphql-java/graphql-java/blob/2acb557474ca73/src/main/java/graphql/schema/FieldCoordinates.java)

- GraphiQL displays field coordinates in its documentation search tab:

![](https://i.fluffy.cc/5Cz9cpwLVsH1FsSF9VPVLwXvwrGpNh7q.png)

- [GraphQL Inspector](https://github.com/kamilkisiela/graphql-inspector) (840 stars) shows type/field pairs in its output:

![](https://i.imgur.com/HAf18rz.png)

- [Apollo Studio](https://www.apollographql.com/docs/studio/) shows field
coodinates when hovering over fields in a query:

![](https://i.fluffy.cc/g78sJCjCJ0MsbNPhvgPXP46Kh9knBCKF.png)

## 🗳️ Alternatives considered

### Naming

- "type/field pairs" was the original suggestion

However, "field coordinates" was chosen as it is already understood and used by
by the popular [GraphQL Java](https://github.com/graphql-java/graphql-java)
project.

### Seperator

This RFC proposes using "`.`" as the seperator character. The following have also
been proposed:

- `Foo::bar`
- `Foo#bar`
- `Foo->bar`
- `Foo~bar`
- `Foo:bar`

"`.`" is already used in the existing implemenations of field coordinates, hence
the suggested usage in this RFC. However, we may wish to consider one of the
alternatives above, should this conflict with existing or planned language
features.

## 🤔 Drawbacks / Open questions

- https://github.com/graphql/graphql-spec/issues/735 discusses potential
conflicts with the upcoming namspaces proposal - would like to seek clarity on
this

- **Is this extensible enough?** The above issue discusses adding arguments as
part of this specifcation - we haven't touched on this here in order to keep
this RFC small, but we may wish to consider this in the future (e.g.
`Query.searchBusiness:name`).

- **How will this play with namespaces?** Not sure what this looks like yet!

- **Would we want to add a method to graphql-js?** A `fieldCoordinateToFieldNode`
method (for example) may take in a field coordinate string and return a field
AST node to serve as a helper / reference implementation of the algorithm to
look up the field node.
103 changes: 103 additions & 0 deletions spec/Appendix C -- Tooling Conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# C. Tooling Conventions

This specification document formalizes naming and conventions that may be used by
GraphQL tooling and third party libraries.


## Field Coordinates

FieldCoordinate : TypeName `.` FieldName

A field coordinate may be used to uniquely identify a field in a GraphQL schema.
It is encoded as a string containing a dot seperated parent type and field pair.

**Examples**

Consider the following schema:

```graphql example
type Person {
name: String
age: Int
}

type Business {
name: String
owner: Person
}
```

In order to refer to the `name` field on the `Business` type, we would write the
following field coordinate:

```example
Business.name
```

Simiarly, in order to refer to the `name` field on the `Person` type, we would
write the following field coordinate:

```example
Person.name
```

**Formal Specification**

* Split the field coordinate string by {`.`}
* Let {typeName} and {fieldName} be the zeroth and first results respectively
* {typeName} must be defined in the schema as a {Name} attribute of {ObjectTypeDefinition}
* {fieldName} must be defined as a {Name} attribute in a {FieldDefinition} of the {FieldsDefinition} attribute of the {ObjectTypeDefinition} found above

## Converting a FieldNode to a Field Coordinate

Given a matching schema, we may wish to refer to a field used in a query document
using a field coordinate.

**Example**

Consider the following schema:

```graphql example
type Person {
name: String
age: Int
}

type Business {
name: String
owner: Person
}

type Query {
searchBusinesses(name: String): [Business]
}
```

Also consider the following document:

```graphql example
query {
searchBusinesses(name: "El Greco Deli") {
name
owner {
name
}
}
}
```

We can calculate that the list of field coordinates referenced in this query is
as follows:

```example
Query.searchBusinesses
Business.name
Business.owner
Person.name
```

**Formal Specification**

TODO: Write up an algorithm

(code implementation: https://github.com/sharkcore/extract-field-coordinates/blob/master/src/extract-field-paths.js)
2 changes: 2 additions & 0 deletions spec/GraphQL.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,5 @@ Note: This is an example of a non-normative note.
# [Appendix: Notation Conventions](Appendix%20A%20--%20Notation%20Conventions.md)

# [Appendix: Grammar Summary](Appendix%20B%20--%20Grammar%20Summary.md)

# [Appendix: Tooling Conventions](Appendix%20C%20--%20Tooling%20Conventions.md)

0 comments on commit 858a105

Please sign in to comment.