graphql-spec
graphql-spec copied to clipboard
[RFC] Collections / CollectionTypeDefinition
TL;DR:
This RFC introduces a new variant of Lists/Arrays that has named entries key-value pairs. Which can be queried exactly like arrays, but a result is an object.
Problem Statement
If a type object has multiple fields of the same type and it makes sense that they are grouped together and be addressed as a collective instead of being addressed individually, with GraphQL you have three ways you can achieve that. Creating a scalar, creating another type, or using an array and somewhere in the documentation mention which position of an array refers to which named entry.
Example
Consider the following requirements:
- a
Buildobject consists of 4Item-slots. - Slot position matters and it should be readable which slots are equipped.
- Preferably slots should be grouped together under a common ancestor without any unrelated siblings.
- Item slots should not be queried/selected individually, the query either includes all slots or non.
- It's possible to specify which
Itemfields are included.
Initially, the schema may look like this:
type Item {
id: String!
name: String!
icon(size: String): String!
}
type Build {
id: String!
slot0Item: Item
slot1Item: Item
slot2Item: Item
slot3Item: Item
}
... but that doesn't satisfy the 3rd and 4th requirements.
Introducing a new type ItemSlots still won't satisfy the 4th requirement:
type Item {
id: String!
name: String!
icon(size: String): String!
}
type ItemSlots {
slot0Item: Item
slot1Item: Item
slot2Item: Item
slot3Item: Item
}
type Build {
id: String!
items: ItemSlots!
}
and using a Scalar it's not possible to meet the 5th requirement.
🧑💻 Proposed Solution
A new collection type.
🎬 Behavior
Collections can be queried exactly as if they were an array/list, but instead of returning an array of 0 to ∞ entries, it returns finite named entries as specified in the schema.
✏️ Proposed syntax - !
Collections can be defined similarly to how an object type is defined, except fields don't have a type (because all fields are supposed to be of the same type).
collection ItemSlots {
slot0
slot1
slot2
slot3
}
type Item {
id: String!
name: String!
icon(size: String): String!
}
type Build {
id: String!
items: ItemSlots<Item>! # or `ItemSlots<Item!>!` if fields are not nullable.
}
✨ Use cases
- Allows grouping of fields that share the same type and are supposed to be addressed atomically.
- Gets rid of unnecessary/repetitive lines in queries to select the same nested fields across multiple fields.
The items field in the above schema can be queried like:
query GetBuild($id: String!, $iconSize: String!) {
build(id: $id) {
id
items {
name
icon(iconSize: $iconSize)
}
}
}
and the result should look something like this:
{
"data": {
"build": {
"id": "...",
"items": {
"slot0": {
"name": "...",
"icon": "..."
},
"slot1": "...",
"slot2": "...",
"slot3": "..."
}
}
}
}
I should probably note that the template structure of this issue was copied from an RFC of @\twof. I just found the template very clean and the structure fits my proposal as well. :))
I do not quite understand the purpose and benefits. The whole reasoning is based on these 'requirements', which are quite questionable. Like number 4:
- Item slots should not be queried/selected individually, the query either includes all slots or non.
why this thing is so necessary (in certain cases?) that it warrants a new concept and new syntax?! Is it like transactional - all or nothing? all fields or none? What is the case when it's critical?!
and req 5, along with the suggested benefit:
Gets rid of unnecessary/repetitive lines in queries to select the same nested fields across multiple fields.
there is a thing already that does this. It's called Fragments