From 858a105bacc13f9a2a23ccc8ca5eba4ab7eef41f Mon Sep 17 00:00:00 2001 From: Mark Larah Date: Fri, 3 Jul 2020 14:29:30 -0700 Subject: [PATCH] RFC: Field coordinates --- rfcs/FieldCoodinateSerialization.md | 150 ++++++++++++++++++++++ rfcs/FieldCoordinates.md | 122 ++++++++++++++++++ spec/Appendix C -- Tooling Conventions.md | 103 +++++++++++++++ spec/GraphQL.md | 2 + 4 files changed, 377 insertions(+) create mode 100644 rfcs/FieldCoodinateSerialization.md create mode 100644 rfcs/FieldCoordinates.md create mode 100644 spec/Appendix C -- Tooling Conventions.md diff --git a/rfcs/FieldCoodinateSerialization.md b/rfcs/FieldCoodinateSerialization.md new file mode 100644 index 000000000..7e4e0ec18 --- /dev/null +++ b/rfcs/FieldCoodinateSerialization.md @@ -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. diff --git a/rfcs/FieldCoordinates.md b/rfcs/FieldCoordinates.md new file mode 100644 index 000000000..52ce65632 --- /dev/null +++ b/rfcs/FieldCoordinates.md @@ -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. diff --git a/spec/Appendix C -- Tooling Conventions.md b/spec/Appendix C -- Tooling Conventions.md new file mode 100644 index 000000000..053141b57 --- /dev/null +++ b/spec/Appendix C -- Tooling Conventions.md @@ -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) \ No newline at end of file diff --git a/spec/GraphQL.md b/spec/GraphQL.md index 3633f4351..1e337142f 100644 --- a/spec/GraphQL.md +++ b/spec/GraphQL.md @@ -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)