Skip to content

Commit 1e4fb92

Browse files
committed
merged fields blob post
1 parent 993554d commit 1e4fb92

File tree

1 file changed

+219
-0
lines changed

1 file changed

+219
-0
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
+++
2+
title = "GraphQL Deep Dive - Part 1: merged fields"
3+
author = "Andreas Marek"
4+
tags = []
5+
categories = []
6+
date = 2019-01-22T00:00:00+10:00
7+
+++
8+
9+
# GraphQL Deep Dive series
10+
11+
Welcome to the new series "GraphQL deep dive" where we will explore advanced or unknown GraphQL topics. The plan is to discuss things mostly in a language and implementation neutral way, even if it is hosted on graphql-java.com.
12+
13+
# Merged Fields
14+
15+
First thing we are looking at is "merged fields".
16+
17+
GraphQL allows for a field to be declared multiple times in a query as long as it can be merged.
18+
19+
Valid GraphQL queries are:
20+
21+
{{< highlight graphql "linenos=table" >}}
22+
{
23+
foo
24+
foo
25+
}
26+
{{< / highlight >}}
27+
28+
<p/>
29+
30+
{{< highlight graphql "linenos=table" >}}
31+
{
32+
foo(id: "123")
33+
foo(id: "123")
34+
foo(id: "123")
35+
}
36+
{{< / highlight >}}
37+
<p/>
38+
39+
{{< highlight graphql "linenos=table" >}}
40+
{
41+
foo(id: "123") {
42+
id
43+
}
44+
foo(id: "123") {
45+
name
46+
}
47+
foo(id: "123") {
48+
id
49+
name
50+
}
51+
}
52+
{{< / highlight >}}
53+
54+
Each of these queries will result in a result with just one "foo" key, not two or three.
55+
56+
Invalid Queries are:
57+
58+
{{< highlight graphql "linenos=table" >}}
59+
{
60+
foo
61+
foo(id: "123")
62+
}
63+
{{< / highlight >}}
64+
<p/>
65+
{{< highlight graphql "linenos=table" >}}
66+
{
67+
foo(id: "123")
68+
foo(id: "456", id2: "123")
69+
}
70+
{{< / highlight >}}
71+
<p/>
72+
{{< highlight graphql "linenos=table" >}}
73+
{
74+
foo(id: "123")
75+
foo: foo2
76+
}
77+
{{< / highlight >}}
78+
79+
The reason whey they are not valid, is because the fields are different: in the first two examples the arguments differ and the third query actually has two different fields under the same key.
80+
81+
# Motivation
82+
83+
The examples so far don't seem really useful, but it all makes sense when you add fragments:
84+
85+
{{< highlight graphql "linenos=table" >}}
86+
{
87+
...myFragment1
88+
...myFragment2
89+
}
90+
91+
fragment myFragment1 on Query {
92+
foo(id: "123") {
93+
name
94+
}
95+
}
96+
fragment myFragment2 on Query {
97+
foo(id: "123") {
98+
url
99+
}
100+
}
101+
102+
{{< / highlight >}}
103+
104+
<p/>
105+
Fragments are designed to be written by different parties (for example different components in a UI) which should not know anything about each other. Requiring that every field can only be declared once would make this objective unfeasible.
106+
107+
But by allowing it as long as the fields are the same allows fragments to be authored in an independent from each other.
108+
109+
# Rules when fields can be merged
110+
111+
The specific details when fields can be merged are written down in [Field Selection Merging](https://facebook.github.io/graphql/draft/#sec-Field-Selection-Merging) in the spec.
112+
113+
The rules are what you would expect in general and they basically say that fields must be the same. The following examples are taken from the spec and they are all valid:
114+
115+
{{< highlight graphql "linenos=table" >}}
116+
fragment mergeIdenticalFields on Dog {
117+
name
118+
name
119+
}
120+
fragment mergeIdenticalAliasesAndFields on Dog {
121+
otherName: name
122+
otherName: name
123+
}
124+
fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
125+
doesKnowCommand(dogCommand: SIT)
126+
doesKnowCommand(dogCommand: SIT)
127+
}
128+
fragment mergeIdenticalFieldsWithIdenticalValues on Dog {
129+
doesKnowCommand(dogCommand: $dogCommand)
130+
doesKnowCommand(dogCommand: $dogCommand)
131+
}
132+
{{< / highlight >}}
133+
134+
The most complex case happens when you have fields in fragments on different types:
135+
136+
{{< highlight graphql "linenos=table" >}}
137+
fragment safeDifferingFields on Pet {
138+
... on Dog {
139+
volume: barkVolume
140+
}
141+
... on Cat {
142+
volume: meowVolume
143+
}
144+
}
145+
{{< / highlight >}}
146+
147+
This is normally invalid because `volume` is an alias for two different fields `barkVolume` and `meowVolume` but because only one of the some are actually resolved and they both return a value of the same type (we assume here that `barkVolume` and `meowVolume` are both of the same type) it is valid.
148+
149+
{{< highlight graphql "linenos=table" >}}
150+
fragment safeDifferingArgs on Pet {
151+
... on Dog {
152+
doesKnowCommand(dogCommand: SIT)
153+
}
154+
... on Cat {
155+
doesKnowCommand(catCommand: JUMP)
156+
}
157+
}
158+
{{< / highlight >}}
159+
160+
This is again a valid case because even if the first `doesKnowCommand` has a different argument than the second `doesKnowCommand` only one of them is actually resolved.
161+
162+
In the next example `someValue` has different types (we assume that `nickname` is a `String` and `meowVolume` is a `Int`) and therefore the query is not valid:
163+
164+
{{< highlight graphql "linenos=table" >}}
165+
fragment conflictingDifferingResponses on Pet {
166+
... on Dog {
167+
someValue: nickname
168+
}
169+
... on Cat {
170+
someValue: meowVolume
171+
}
172+
}
173+
{{< / highlight >}}
174+
175+
# Sub selections and directives
176+
177+
One thing to keep in my mind is that the sub selections of fields are merged together. For example here `foo` is resolved once and than `id` and `name` is resolved.
178+
179+
{{< highlight graphql "linenos=table" >}}
180+
{
181+
foo(id: "123") {
182+
id
183+
}
184+
foo(id: "123") {
185+
name
186+
}
187+
}
188+
{{< / highlight >}}
189+
190+
This query is the same as:
191+
192+
{{< highlight graphql "linenos=table" >}}
193+
{
194+
foo(id: "123") {
195+
id
196+
name
197+
}
198+
}
199+
{{< / highlight >}}
200+
201+
The second thing to keep in mind is that different directives can be on each field:
202+
203+
{{< highlight graphql "linenos=table" >}}
204+
{
205+
foo(id: "123") @myDirective {
206+
id
207+
}
208+
foo(id: "123") @myOtherDirective {
209+
name
210+
}
211+
}
212+
{{< / highlight >}}
213+
214+
# Merged fields in graphql-js and GraphQl Java
215+
216+
In graphql-js merged fields are relevant when you implement a resolver and you need access to the specific ast field of the query. The `info` objects has a property `fieldNodes` which gives you access to all ast fields which are merged together.
217+
218+
In GraphQL Java depending on the version you are running you have `List<Field> getFields()` in the `DataFetcherEnvironment` or for GraphQL Java newer than `12.0` you have also `MergedField getMergedField()` which is the recommend way to access all merged fields.
219+

0 commit comments

Comments
 (0)