-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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: support returning applied directives as part of the introspection #1075
RFC: support returning applied directives as part of the introspection #1075
Conversation
✅ Deploy Preview for graphql-spec-draft ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
c01e0a7
to
cb9ee1d
Compare
Co-authored-by: Martin Bonnin <[email protected]>
Excellent to see this written up in spec edits; thanks for the work @JoviDeCroock 🙌 Relevant: Lee's essentially equivalent proposal: graphql/graphql-wg#1096 Despite the simplicity of the solution and the fact it's already used in the ecosystem, unfortunately I don't see using the Bundle size and complexity increasesClients that wish to consume the directive values will need to bundle a parser, increasing bundle size. It's likely that a simple parser could be built in not much code, so I'm not terribly worried about this, but it is something to keep in mind. Handling of custom scalarsCustom scalars have their own It prevents syntax expansionThis, to me, is the biggest show-stopper issue. Because these values need parsing, the parsing becomes a position where breaking changes could occur. If a new syntax was introduced then this new syntax may not be parseable by existing clients, causing unexpected errors. (For example, imagine we added an optional additional argument to an existing directive, and this new argument was of a new type which used a syntax the existing parser did not understand.) Doing this would essentially fix the syntax permanently, which fails the "Preserve option value" guiding principle. I digested the existing solutions proposed for #300, including the approach that you've outlined, in this talk at GraphQL Conf 2022: https://youtu.be/FC4AFSjh_4k?t=289 Here's another presentation where I talk about the need for granular metadata in GraphQL: An alternative solution that I raised a while back is "annotation structs": https://github.com/graphql/graphql-wg/blob/main/rfcs/AnnotationStructs.md This solution enables querying schema metadata using regular fields to represent the arguments, so scalars will appear the same in introspection as they would when queried through the non-introspection parts of your schema - there's no need for a parser. Further, the solution allows you to query just parts of the metadata, so that your client does not get overwhelmed - this is useful in similar situations to where you might introspect an enum in your client to find out the list of enumValues to render in a dropdown. The main issue that the annotation structs proposal faces right now (other than that it relies on the * The struct proposal is held up because it solves too many problems 😉 |
Thanks @benjie for the detailed writeup, as always! One thing that is still not 100% clear to me after watching the video is the custom scalar part:
I would expect this? Because custom scalars are not specified, how to interpret them has to be passed out of band. For an example say I have the (completely synthetic) example below: scalar EmailAddress
"""
The owners of this type. Owners are notified any time a change is requrested and must approve changes
"""
directive @owners(emails: [EmailAddress]) on OBJECT
type Product @owners(emails: ["[email protected]"]) {
name: String!
description: String!
price: Float!
}
|
I generally agree with the points made by @martinbonnin external providers can declare directives like I don't disagree with having a deeper solution as you are describing @benjie which seems to get us to a wider benefit of these values similar to how this could then be From a backwards/forwards compatibility perspective the change isn't very reversible as we would make it part of the schema, this however made me think if we could somehow make this a point of extension rather than embedding it in the introspection. |
@martinbonnin Right; but you are now giving two separate representations of this scalar - the representation that you'd get from querying it in the output schema, and now this new stringified form which may or may not match that when parsed. The latter may require additional work that the former would not. (Your example would have the same representation both ways, so does not suffer from this complexity; but as a spec we must handle the edge cases.) For example, if your custom scalar represented a This is probably less of an issue when GraphQL is used over a JSON-based transport since GraphQL's language structure and JSON's are very similar (types: object, list, string, number, boolean) but if you use it with something that has more specific typings this could be significant. For example, a GraphQL response over a different transport could encode your |
@benjie Right 👍 So the issue is that the client has to implement I think I'd be ok with that, especially since we're saying the client has to bundle a GraphQL value parser anyways (and therefore be "GraphQL value aware"). But yea, I can see how that's definitely a limitation to account for in the final tradeoff. |
@martinbonnin Clients don't currently implement parseLiteral or parseValue - these are server-side functions used to interpret the data sent from clients to the server. When clients receive scalars they typically use this data directly without any transformation; we would be introducing the need for transformation, and that transformation would need to be defined for each custom GraphQL scalar type in use. |
@benjie True that Adapters adapts from JSON to "runtime object". This way clients can use the type system they are used to. Typical example is The Apollo Kotlin client doesn't have anything to transform from GraphQL value to "runtime object" (~= Of course, it's not great because it means |
Yes, I'm a huge proponent of metadata directives and have done a lot of research and work on the topic. I'm definitely not arguing against them, just against the specific stringified way of expressing them when we could just use the scalars directly if we figure out how. |
Closing this in favour of the metadata proposal as highlighted in the above discussion |
The following proposes an addition to the introspection part of the spec. In every place where a directive can be applied we alter the corresponding type to have a list of
__AppliedDirective
present.This would support use cases where we need additional information about the field to facilitate functionality like
@sensitive
- to mask sensitive fields from logging information@cache
- to instruct an external system that a field is cacheableSDL
under a field so the central router can parse directivesThis is already implement in the following implementations
I tried making this as backwards compatible as possible, an alternative would be to rather than using
name
referring to the__Directive
. Or if wanted we could includetype
with a reference to__Directive
this does not seem present in the Java and .NET implementation.I am aware of the limitation that
String
imposes as a type for the.value
my main reasoning was that it's very much in line withdefaultValue
and shouldn't pose that much of an issue.I am open to implementing this in the JS reference implementation as well.
Related to #300