forked from graphql/graphql-wg
-
Notifications
You must be signed in to change notification settings - Fork 0
RFC: Object Identification #1
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
Draft
phryneas
wants to merge
5
commits into
target
Choose a base branch
from
pr/rfc-object-identification
base: target
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+88
−0
Draft
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
46437e9
RFC: Object Identification
phryneas 6b6c02c
Update rfcs/ObjectIdentification.md
phryneas 66a382b
Update rfcs/ObjectIdentification.md
phryneas 058a4c6
Update rfcs/ObjectIdentification.md
phryneas 0b43872
remove confusing attempt at clarification
phryneas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# RFC: Object Identification | ||
|
||
**Proposed by:** [Lenz Weber-Tronic](https://github.com/phryneas) - Apollo | ||
|
||
## Problem statement | ||
|
||
Currently, there is no way for clients to know if an object can be uniquely identified. This makes it hard for clients to implement caching strategies that rely on object identity for normalization, or for AI agents that need to communicate with other systems, referencing objects returned from a GraphQL API. | ||
|
||
This is in part solved by patterns like [Global Object Identification](https://graphql.org/learn/global-object-identification/), but this is nothing clients can generally rely on, since there is no guarantee that a server actually follows this pattern or just accidentally overlaps with it, without actually fully implementing it. | ||
|
||
Also, the Global Object Identification pattern is not enough for the use case of a Client without schema knowledge - such a client couldn't inject a selection for `id` into all object selections (as they usually do with `__typename`), since it doesn't know which objects actually have an `id` field and the resulting query might be invalid. | ||
|
||
## Proposal | ||
|
||
### Introduction of an `__id` introspection field | ||
|
||
This proposal introduces a new introspection field `__id`, which can be queried on any object, interface or union type. | ||
The field is of type `ID` and for each individual type, the field must either always return a non-null value or always return null. | ||
|
||
If an `__id` field returns a non-null value for a type, this value must be guaranteed to uniquely identify the object when combined with the value of the `__typename` field. | ||
|
||
This would allow clients without schema knowledge to query for `__id` on selection set and use the returned value for caching or referencing the object in other systems, if it is not `null`. | ||
As a result, this would remove the need for manual configuration like Apollo Client's [`keyFields` type policies](https://www.apollographql.com/docs/react/caching/cache-configuration#customizing-cache-ids) or urqls's [Custom Keys](https://nearform.com/open-source/urql/docs/graphcache/normalized-caching/#custom-keys-and-non-keyable-entities), which are currently needed to tell the client which fields to use for identifying an object. | ||
These configurations are often a source of bugs, since they can be forgotten or misconfigured, or simply may not keep up with an evolving schema. | ||
|
||
### Intoduction of an `__Entity` interface | ||
|
||
In addition to the `__id` field, this proposal also introduces a new introspection interface `__Entity`, which is defined as follows: | ||
|
||
```graphql | ||
interface __Entity { | ||
__id: ID! | ||
} | ||
``` | ||
|
||
This interface could be used by clients with schema knowledge (such as Apollo Kotlin or Apollo iOS) at build time to decide if a certain type can be uniquely identified (and thus, stored in a normalized way) or not. | ||
|
||
This interface may be implicitly added to objects that implement a way to resolve the `__id` field to a non-null value, depending on the server implementation. | ||
Alternatively, implementers might also choose to explicitly add the interface to types that can be uniquely identified. | ||
|
||
### Presence of globally identifiable types in the schema | ||
|
||
To allow clients to detect if a server supports this feature, the value of the `__id` field on the `Query` type should be specified as well: | ||
|
||
* If a server does not have any types with a non-null `__id` field, the `__id` field on the `Query` type should return `null`. | ||
* If a server has at least one type with a non-null `__id` field, the `__id` field on the `Query` type should return a non-null value (the suggestion would be `"ROOT_QUERY"`, if we are to specify the exact value). | ||
|
||
This could be used by clients to detect if the server supports the feature at all, and if it doesn't, they could choose to omit querying for the `__id` field in future requests to save bandwidth. | ||
|
||
|
||
## Suggested Spec addition | ||
|
||
To be inserted after the "Type Name Introspection" section in `spec/Section 4 -- Introspection.md` | ||
|
||
````md | ||
## Object Identification | ||
|
||
GraphQL supports Object Identification via the meta-field `__id: ID` on any | ||
Object, Interface, or Union. | ||
|
||
This field returns: | ||
* `null`, if the object does not have a unique identifier in the context of its | ||
type, or if the schema doesn't support Object Identification in general. | ||
* a non-null ID that in combination with the object's type name is globally unique. | ||
|
||
For every type, `__id` must either always return `null` or never return `null`. | ||
|
||
As a meta-field, `__id` is implicit and does not appear in the fields list in | ||
any defined type. | ||
|
||
The value of `__id` on the `Query` type is defined to either be `ROOT_QUERY` if | ||
the schema supports Object Identification, or `null` if it does not. This guarantee | ||
can be used to introspect whether a schema supports Object Identification in general. | ||
|
||
Note: `__id` may not be included as a root field in a subscription operation. | ||
|
||
## Identifiable types | ||
|
||
Identifiable types may also implement the `__Entity` interface, which is defined as follows: | ||
|
||
```graphql | ||
interface __Entity { | ||
__id: ID! | ||
} | ||
``` | ||
|
||
A server might choose to implicitly add this interface to types that implement a way to resolve the `__id` field to a non-null value. | ||
```` |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__id
is specified asID
and overridden asID!
- I believe this makes sense (ID!
being a subtype ofID
) but potentially a can of worms 😅There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's an entity, it's identifiable. This goes hand in hand with that requirement of "for a type,
__id
is either always null or never null`There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the world of
graphql-js
for example this could be "if you define a resolver/calculation function for__id
, it may never returnnull
and we implicitly apply__Entity
to the type.