Skip to content

Commit 946eb2c

Browse files
spawniabinaryseed
authored andcommitted
Evaluate solutions against criteria (#650)
* Evaluate solutions against criteria * Evaluate against named links, include existing problem statements * Add comparison matrix * Add negative for solution 3 as suggested by @benjie * Remove empty evaluations
1 parent 0d31e7b commit 946eb2c

File tree

1 file changed

+114
-33
lines changed

1 file changed

+114
-33
lines changed

rfcs/InputUnion.md

Lines changed: 114 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,10 @@ Any data structure that can be modeled with output type polymorphism should be a
181181

182182
* Objection: composite input types and composite output types are distinct. Fields on composite output types support aliases and arguments whereas fields on composite input types do not. Marking an output field as non-nullable is a non-breaking change, but marking an input field as non-nullable is a breaking change.
183183

184-
### C) Doesn't inhibit [schema evolution](https://graphql.github.io/graphql-spec/draft/#sec-Validation.Type-system-evolution)
184+
### C) Doesn't inhibit schema evolution
185+
186+
The GraphQL specification mentions the ability to evolve your schema as one of its core values:
187+
https://graphql.github.io/graphql-spec/draft/#sec-Validation.Type-system-evolution
185188

186189
Adding a new member type to an Input Union or doing any non-breaking change to existing member types does not result in breaking change. For example, adding a new optional field to member type or changing a field from non-nullable to nullable does not break previously valid client operations.
187190

@@ -244,16 +247,33 @@ There have been a variety of use cases described by users asking for an abstract
244247

245248
## Possible Solutions
246249

247-
Broadly speaking, there are two categories of solutions to the problem of type discrimination:
248-
249-
* Value-based discriminator field
250-
* Structural discrimination
251-
252-
### Value-based discriminator field
253-
254-
These solutions rely the **value** of a specific input field to determine the concrete type.
255-
256-
#### Single `__typename` field; value is the `type`
250+
Each of the solutions should be evaluated against each of the criteria.
251+
252+
To get an overview, the following table broadly rates how good the solution fulfils
253+
a certain criteria. Positive is always *good*, so if a criteria is something bad and
254+
the solution does avoid the negative aspact, it gets a o
255+
- `o` Positive
256+
- `-` Neutral/Not applicable
257+
- `x` Negative
258+
259+
| | 1 | 2 | 3 | 4 | 5 |
260+
|---|---|---|---|---|---|
261+
| A | | | | | |
262+
| B | | | | | x |
263+
| C | | | x | x | |
264+
| D | | | | | |
265+
| E | | | | | |
266+
| F | | | | | |
267+
| G | | | | | |
268+
| H | x | x | | | |
269+
| I | | | | | |
270+
| J | | | | | |
271+
| K | | | | | |
272+
| L | x | x | | | |
273+
274+
More detailed evaluations are to be placed in each solution's description.
275+
276+
### 1. Single `__typename` field; value is the `type`
257277

258278
This solution was discussed in https://github.com/graphql/graphql-spec/pull/395
259279

@@ -288,11 +308,21 @@ type Mutation {
288308
}
289309
```
290310

291-
##### Variations:
311+
#### Variations:
292312

293313
* A `default` type may be defined, for which specifying the `__typename` is not required. This enables a field to migration from an `Input` to an `Input Union`
294314

295-
#### Single user-chosen field; value is the `type`
315+
#### Evaluation
316+
317+
##### [Input unions should accept plain data from clients](#h-input-unions-should-accept-plain-data-from-clients)
318+
319+
No, as there is an additional field that has to be added.
320+
321+
##### [Input unions should be expressed efficiently in the query and on the wire](#k-input-unions-should-be-expressed-efficiently-in-the-query-and-on-the-wire)
322+
323+
The additional field bloats the query.
324+
325+
### 2. Single user-chosen field; value is the `type`
296326

297327
```graphql
298328
input CatInput {
@@ -327,15 +357,11 @@ type Mutation {
327357
}
328358
```
329359

330-
##### Problems:
331-
332-
* The discriminator field is non-sensical if the input is used _outside_ of an input union, or in multiple input unions.
333-
334-
##### Variations:
360+
#### Variations:
335361

336362
* Value is a `Enum` literal
337363

338-
This solution is derrived from discussions in https://github.com/graphql/graphql-spec/issues/488
364+
This solution is derived from discussions in https://github.com/graphql/graphql-spec/issues/488
339365

340366
```graphql
341367
enum AnimalKind {
@@ -386,15 +412,17 @@ input DogInput {
386412
}
387413
```
388414

389-
##### Problems:
415+
#### Evaluation
416+
417+
##### [Input unions should accept plain data from clients](#h-input-unions-should-accept-plain-data-from-clients)
390418

391-
* The discriminator field is redundant if the input is used _outside_ of an input union.
419+
No, as there is an additional field that has to be added.
392420

393-
### Structural discrimination
421+
##### [Input unions should be expressed efficiently in the query and on the wire](#k-input-unions-should-be-expressed-efficiently-in-the-query-and-on-the-wire)
394422

395-
These solutions rely on the **structure** of the input to determine the concrete type.
423+
The additional field bloats the query.
396424

397-
#### Order based type matching
425+
### 3. Order based type matching
398426

399427
The concrete type is the first type in the input union definition that matches.
400428

@@ -435,7 +463,58 @@ type Mutation {
435463
}
436464
```
437465

438-
#### Structural uniqueness
466+
#### Evaluation
467+
468+
##### [Doesn't inhibit schema evolution](#c-doesnt-inhibit-schema-evolution)
469+
470+
Adding a nullable field to an input object could change the detected type of
471+
fields or arguments in pre-existing operations.
472+
473+
Assuming we have this schema:
474+
475+
```graphql
476+
input InputA {
477+
foo: Int
478+
}
479+
input InputB {
480+
foo: Int
481+
bar: Int
482+
}
483+
input InputC {
484+
foo: Int
485+
bar: Int
486+
baz: Int
487+
}
488+
inputUnion InputU = InputA | InputB | InputC
489+
490+
type A {...}
491+
type B {...}
492+
type C {...}
493+
union U = A | B | C
494+
495+
type Query {
496+
field(u: InputU): U
497+
}
498+
```
499+
500+
Given this query:
501+
502+
```graphql
503+
query ($u: InputU = {foo: 3, baz: 3}) {
504+
field(u: $u) {
505+
__typename
506+
... on C {
507+
# ...
508+
}
509+
}
510+
}
511+
```
512+
513+
the default value of `$u` would be detected as type InputC. However adding the
514+
field `baz: Int` to type InputA would result in the same value now being detected
515+
as type InputA, which could be a breaking change for application behaviour.
516+
517+
### 4. Structural uniqueness
439518

440519
Schema Rule: Each type in the union must have a unique set of required field names
441520

@@ -491,16 +570,15 @@ input DogInput {
491570
}
492571
```
493572

494-
##### Problems:
495-
496-
* Inputs may be pushed to include extraneous fields
573+
#### Variations:
497574

575+
* Consider the field _type_ along with the field _name_ when determining uniqueness.
498576

499-
##### Variations:
577+
##### [Doesn't inhibit schema evolution](#c-doesnt-inhibit-schema-evolution)
500578

501-
* Consider the field _type_ along with the field _name_ when determining uniqueness.
579+
Inputs may be pushed to include extraneous fields to ensure uniqueness.
502580

503-
#### One Of (Tagged Union)
581+
### 5. One Of (Tagged Union)
504582

505583
This solution was presented in:
506584
* https://github.com/graphql/graphql-spec/pull/395#issuecomment-361373097
@@ -543,6 +621,9 @@ type Mutation {
543621
}
544622
```
545623

546-
##### Problems:
624+
#### Evaluation
625+
626+
##### [Input polymorphism matches output polymorphism](#b-input-polymorphism-matches-output-polymorphism)
547627

548-
* Forces data to be modeled with different structure for input and output types.
628+
The shape of the input type is forced to have a different structure than the
629+
corresponding output type.

0 commit comments

Comments
 (0)