Skip to content
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

[RFC] Custom Scalar Specification URLs #649

Merged
merged 25 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5a2e3e3
First draft of spec changes for the scalar RFC
eapache Aug 7, 2019
2e24437
Add a bit about changing the URI
eapache Aug 15, 2019
1d1cf95
Clarify some points raised at working group
eapache Nov 11, 2019
22d2921
switch to a directive
eapache Nov 20, 2019
9a599f6
Minor fixes based on feedback
eapache Nov 26, 2019
bc9a408
Use "URL" instead of "RFC3986-compliant URI"
eapache Dec 10, 2019
2c2057e
Tweak introspection language
eapache Dec 10, 2019
a620718
Use UUID as a less controversial example
eapache Dec 10, 2019
5522c9a
Update directive name to @specifiedBy
eapache Dec 16, 2019
2ed5043
Tweak language
eapache Dec 16, 2019
50b5510
Americanize spelling in spec/Section 3 -- Type System.md
eapache Feb 5, 2020
e235752
Add link to RFC4122
eapache Feb 5, 2020
42360e4
Update spec/Section 4 -- Introspection.md
eapache Feb 5, 2020
4b5a285
Minor wording and formatting tweaks
eapache Feb 5, 2020
968f961
Editorial
leebyron Jan 11, 2021
6c58094
Editorial for Scalars section
leebyron Jan 11, 2021
bab4fab
Editorial to reduce duplication and centralize info
leebyron Jan 11, 2021
038b6f8
Editorial - grammar
leebyron Jan 11, 2021
eb18c03
Editorial - simplify introspection change
leebyron Jan 11, 2021
96fb0d1
Editorial - introspection description
leebyron Jan 11, 2021
f8065c3
Editorial - return note about use of descriptions
leebyron Jan 11, 2021
a2401b7
Editorial - directive description
leebyron Jan 11, 2021
318b753
Update Section 3 -- Type System.md
leebyron Jan 11, 2021
f2bf007
address review
leebyron Apr 9, 2021
89014b9
additional editorial adjustment
leebyron Apr 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 64 additions & 13 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,19 +358,9 @@ Scalar types represent primitive leaf values in a GraphQL type system. GraphQL
responses take the form of a hierarchical tree; the leaves of this tree are
typically GraphQL Scalar types (but may also be Enum types or {null} values).

GraphQL provides a number of built-in scalars (see below), but type systems can
add additional scalars with semantic meaning. For example, a GraphQL system
could define a scalar called `Time` which, while serialized as a string,
promises to conform to ISO-8601. When querying a field of type `Time`, you can
then rely on the ability to parse the result with an ISO-8601 parser and use a
client-specific primitive for time. Another example of a potentially useful
custom scalar is `Url`, which serializes as a string, but is guaranteed by
the service to be a valid URL.

```graphql example
scalar Time
scalar Url
```
GraphQL provides a number of built-in scalars which are fully defined in the
sections below, however type systems may also add additional custom scalars to
introduce additional semantic meaning.

**Built-in Scalars**

Expand All @@ -390,6 +380,43 @@ that type) then it must not be included.
When representing a GraphQL schema using the type system definition language,
all built-in scalars must be omitted for brevity.

**Custom Scalars**

GraphQL services may use custom scalar types in addition to the built-in
scalars. For example, a GraphQL service could define a scalar called `UUID`
which, while serialized as a string, conforms to [RFC 4122](https://tools.ietf.org/html/rfc4122).
When querying a field of type `UUID`, you can then rely on the ability to parse
the result with a RFC 4122 compliant parser. Another example of a potentially
useful custom scalar is `URL`, which serializes as a string, but is guaranteed
by the server to be a valid URL.

When defining a custom scalar, GraphQL services should provide a specification
URL via the `@specifiedBy` directive or the `specifiedBy` introspection field.
This URL must link to a human-readable specification of the data format,
serialization, and coercion rules for the scalar. For example, a GraphQL service
providing a `UUID` scalar may link to RFC 4122, or some custom document defining
a reasonable subset of that RFC. If a scalar `specifiedBy` URL is present,
systems and tools that are aware of it should conform to its described rules.

```graphql example
scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
```

Custom scalar specifications should provide a single, stable format to avoid
ambiguity. If the linked specification is in flux, the service should link to a
fixed version rather than to a resource which might change.

Custom scalar specification URLs should not be changed once defined. Doing so
would likely disrupt tooling or could introduce breaking changes within the
linked specification's contents.

Built-in scalar types must not provide a `specifiedBy` URL as they are specified
by this document.

Note: Custom scalars should also summarize the specified format and provide
examples in their description.

**Result Coercion and Serialization**

A GraphQL service, when preparing a field of a given scalar type, must uphold the
Expand Down Expand Up @@ -1850,6 +1877,10 @@ GraphQL implementations that support the type system definition language must
provide the `@deprecated` directive if representing deprecated portions of
the schema.

GraphQL implementations that support the type system definition language should
provide the `@specifiedBy` directive if representing custom scalar
definitions.

**Custom Directives**

GraphQL services and client tooling may provide additional directives beyond
Expand Down Expand Up @@ -2012,3 +2043,23 @@ type ExampleType {
oldField: String @deprecated(reason: "Use `newField`.")
}
```


### @specifiedBy

```graphql
directive @specifiedBy(url: String!) on SCALAR
```

The `@specifiedBy` directive is used within the type system definition language
to provide a URL for specifying the behavior of
[custom scalar types](#sec-Scalars.Custom-Scalars). The URL should point to a
human-readable specification of the data format, serialization, and
coercion rules. It must not appear on built-in scalar types.

In this example, a custom scalar type for `UUID` is defined with a URL pointing
to the relevant IETF specification.

```graphql example
scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While implementing this in graphql/graphql-js#2276 @IvanGoncharov provided the following comment:

If at some point we decide to add UUID scalar into this library it would be a confusing example.
It's better to use some abstract scalar like Foo and https://example.com/foo_spec.

Question: Do we want to make that same change here as well? If so this likely applies to URL as well, and effects a few locations in this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Point taken that it could become confusing, but it's not confusing yet, and there is definitely value in having a concrete example that lines up with use cases a lot of actual developers will run into. I'm planning to leave this as is, and we can change it if becomes an issue later.

```
9 changes: 7 additions & 2 deletions spec/Section 4 -- Introspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ type __Type {

# should be non-null for NON_NULL and LIST only, must be null for the others
ofType: __Type

# should be non-null for custom SCALAR only, must be null for the others
specifiedBy: String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It correlates to some extent with #300 .
If this extension of the introspection schema will be added, it is logical to expect that the proposed in #300 extension
should be also accepted. Now there is a second server directive besides @deprecated, which is provided through introspection as a special property. I have nothing against this, but I believe that we need to provide a way to get user directives through introspection too. It will be a consistent step.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sungam3r there are some experiments on introspection extensions. Technically you can already today extend introspection but this alway comes with the danger that you violate a future specification.

@IvanGoncharov was experimenting on this one with GraphQL-js. At the moment you can put metadata on almost everything there. We do almost the same with hot chocolate. The next thing would be to allow to have something like extensions on introspection so that there is a point where you can cleanly extend the introspection types.

Doing this through directives brings some challenges since directives, specifically their arguments are input types that could bear challenges to automatically extend the introspection from those.

@IvanGoncharov maybe we should start a new discussion on that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a separate graphql-dotnet branch in which directives are already returned via introspection. We use this forked preview version in production.

this alway comes with the danger that you violate a future specification.

I agree. It’s not scary for us. We need new features now, and not when they (possibly) appear in the specification.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sungam3r i did mean that more from them perspective. Hc allows extending the introspection already for a couple of versions. But the user that does that has to know that he/she might violate a future spec. We just have a warning in the docs that you can do it but may run into an issue in the future.

}

type __Field {
Expand Down Expand Up @@ -238,13 +241,15 @@ actually valid. These kinds are listed in the `__TypeKind` enumeration.

Represents scalar types such as Int, String, and Boolean. Scalars cannot have fields.

A GraphQL type designer should describe the data format and scalar coercion
rules in the description field of any scalar.
Also represents [Custom scalars](#sec-Scalars.Custom-Scalars) which may provide
`specifiedBy` as a scalar specification URL.

Fields

* `kind` must return `__TypeKind.SCALAR`.
* `name` must return a String.
* `specifiedBy` may return a String (in the form of a URL) for custom scalars,
otherwise must be {null}.
* `description` may return a String or {null}.
* All other fields must return {null}.

Expand Down