Description
(This RFC grew out of the discussion around #579 and is championed by @eapache and @andimarek.)
This RFC proposes attaching an optional URI to custom scalar definitions pointing to a document holding data-format, serialization, and coercion rules for the scalar.
Problem
The set of scalars provided within the main GraphQL specification is limited to primitive types (Int
, String
, etc) plus ID
. In the wild, most schemas end up implementing several additional custom scalars for types like Date
and URL
; in fact the GraphQL specification currently recommends those two as examples of motivating use cases for custom scalars, and there have been proposals to add them to the main GraphQL specification (e.g. #315, #579). There are several closely-related problems with the world that has grown around the usage of these custom scalars:
- Schema authors have to pack the entire format of custom scalars into the
description
field which often results in scalar formats being seriously underspecified. Compare the built-in scalar types, which each take several hundred words to define in the current spec. - Clients and tools have no way to introspect which custom scalar conforms to which format, and thus no way to dynamically provide additional functionality based on that information (for example, a date-picker widget for date-based inputs).
- Many subtly different variations of each scalar exist, depending on the precise name and formatting chosen (for example, there are numerous different ways to represent a date and time). This makes interoperability very difficult for clients or tools that have to consume multiple schemas by different authors.
Solution
Allow attaching a URI to custom scalar definitions, in both the SDL and via introspection. If present, the URI is expected to point to a definition of the data-format, serialization, and coercion rules for the scalar. This provides value because:
- The linked page now provides ample space for schema authors to properly specify the format of the scalar.
- Clients and tools can treat the URI as an opaque key and use it to reliably identify the data format being used (for example, recognizing that a scalar linking to RFC 3339 is a DateTime in some format, regardless of its name) in order to provide additional functionality. The URI is considered opaque in this context because client tooling need not be able to follow the link or understand the contents of the linked document, they need only be able to map the URI itself (e.g.
https://tools.ietf.org/html/rfc3339
) to some internal behaviour (e.g. a date-picker UI). - Support for additional scalar formats by common tools (e.g. GraphiQL) and the constraint of providing a specific URI defining the format will hopefully result in a gradual consolidation of the ecosystem around common formats for things like DateTime, without constraining schema authors who want to do something different.
Out of Scope
The following problems are closely related, but considered out of scope for this RFC. They could be tackled in later RFCs if there is interest:
- A standard way of attaching format or specification compliance information to non-scalar types (e.g. "this object is a Relay-style Connection"). This RFC is limited to custom scalar definitions.
- Defining standards for how a scalar format should be specified, what serialization data and information are required, etc. This RFC is limited to the ability to attach a spec to a custom scalar, and does not deal with what that spec should contain.
Alternatives and Variations
- Define more common scalars (DateTime, URL, etc) in the main GraphQL specification alongside Int, String, ID, etc.
- Pro: a much stronger standardization effect on the ecosystem.
- Con: forcing the many production schemas who have existing scalars in the same area to choose between incompatibility, or a breaking schema change for their users.
- Con: it becomes contentious to determine when a format is sufficiently common to bake into the spec (and thus deserves better tooling etc) vs when it isn’t, as there is no middle ground.
- Allow attaching an array of URIs to custom scalar definitions.
- Pro: many RFCs and standards formats evolve through the addition of new RFCs and formats which build upon the past. An array of URIs would allow schemas to indicate support for the proper set of standards rather than trying to pick the single correct document.
- Con: complexity for the majority of cases which don’t need this.
Cost
The primary costs of this RFC are:
- Complexity in the grammar and implementation.
- It is an incompatible change to introspection: clients which adopt the RFC and query the new introspection field will not work with older servers which do not provide it.
Illustrative example
This example specifies a new “DateTime” scalar based on RFC3339. It should be published and hosted somewhere on graphql.org distinct from the main GraphQL spec.
The upstream RFC can be found here: https://www.ietf.org/rfc/rfc3339.txt.
And the errata here: https://www.rfc-editor.org/errata/rfc3339.
RFC3339 has some ambiguities or unclear sections. This spec aims to clear up these ambiguities without introducing new semantics or changing any of the semantics established in RFC3339.
The format for input and output for this scalar is the same.
This DateTime scalar represents a “date-time” as specified in section 5.6.
While RFC3339 allows also other formats (see Errata of the RFC) only “date-time” is accepted in this scalar.
The allowed separation character between “full-date” and “full-time” is “T”.
In RFC3330 “time-secfrac” is optional, and not limited to a specific amount of digits. This scalar requires it to be present with exactly three digits representing milliseconds.
This scalar also doesn’t allow “-00:00” as offset to describe an unknown offset (See 4.3. Unknown Local Offset Convention in the RFC)
Example of valid DateTime scalars:
2011-08-30T13:22:53.108Z
2011-08-30T13:22:53.108-03
2011-08-30T13:22:53.108+03:30
Example of invalid DateTime scalars:
2011-08-30T13:22:53.108912Z -> too many secfrac digits
2011-08-30T13:22:53.108 -> no offset
2011-08-30 -> no time
2011-08-30T13:22:53.108-00:00 -> negative offset representing unknown offset not allowed
Spec edits
Concerns, challenges, and drawbacks
- How do we specify a URL in the spec grammar? The two main options would be to a) simply reference the grammar from RFC3986 or b) only specify it as a string, and let the fact that it is a URL be mostly convention.
- What is the best grammar for specifying the extra field in the SDL. Should we add a keyword, or reuse “implements”, or something?