graphql-stitching 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +58 -424
- data/docs/composing_a_supergraph.md +215 -0
- data/docs/error_handling.md +69 -0
- data/docs/executables.md +112 -0
- data/docs/introduction.md +17 -0
- data/docs/merged_types.md +457 -0
- data/docs/{federation_entities.md → merged_types_apollo.md} +1 -1
- data/docs/performance.md +71 -0
- data/docs/query_planning.md +102 -0
- data/docs/serving_a_supergraph.md +152 -0
- data/docs/subscriptions.md +1 -1
- data/docs/visibility.md +21 -11
- data/lib/graphql/stitching/client.rb +6 -0
- data/lib/graphql/stitching/composer.rb +18 -10
- data/lib/graphql/stitching/request.rb +4 -0
- data/lib/graphql/stitching/supergraph.rb +4 -2
- data/lib/graphql/stitching/version.rb +1 -1
- metadata +11 -11
- data/docs/README.md +0 -19
- data/docs/client.md +0 -107
- data/docs/composer.md +0 -125
- data/docs/http_executable.md +0 -51
- data/docs/mechanics.md +0 -306
- data/docs/request.md +0 -34
- data/docs/supergraph.md +0 -31
- data/docs/type_resolver.md +0 -101
data/docs/http_executable.md
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
## GraphQL::Stitching::HttpExecutable
|
2
|
-
|
3
|
-
A `HttpExecutable` provides an out-of-the-box convenience for sending HTTP post requests to a remote location, or a base class for your own implementation with [GraphQL multipart uploads](https://github.com/jaydenseric/graphql-multipart-request-spec).
|
4
|
-
|
5
|
-
```ruby
|
6
|
-
exe = GraphQL::Stitching::HttpExecutable.new(
|
7
|
-
url: "http://localhost:3001",
|
8
|
-
headers: {
|
9
|
-
"Authorization" => "..."
|
10
|
-
}
|
11
|
-
)
|
12
|
-
```
|
13
|
-
|
14
|
-
### GraphQL file uploads
|
15
|
-
|
16
|
-
The [GraphQL Upload Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) defines a multipart form structure for submitting GraphQL requests with file upload attachments. It's possible to pass these requests through stitched schemas using the following:
|
17
|
-
|
18
|
-
#### 1. Input file uploads as Tempfile variables
|
19
|
-
|
20
|
-
```ruby
|
21
|
-
client.execute(
|
22
|
-
"mutation($file: Upload) { upload(file: $file) }",
|
23
|
-
variables: { "file" => Tempfile.new(...) }
|
24
|
-
)
|
25
|
-
```
|
26
|
-
|
27
|
-
File uploads must enter the stitched schema as standard GraphQL variables with `Tempfile` values. The simplest way to recieve this input is to install [apollo_upload_server](https://github.com/jetruby/apollo_upload_server-ruby) into your stitching app's middleware so that multipart form submissions automatically unpack into standard variables.
|
28
|
-
|
29
|
-
#### 2. Enable `HttpExecutable.upload_types`
|
30
|
-
|
31
|
-
```ruby
|
32
|
-
client = GraphQL::Stitching::Client.new(locations: {
|
33
|
-
alpha: {
|
34
|
-
schema: GraphQL::Schema.from_definition(...),
|
35
|
-
executable: GraphQL::Stitching::HttpExecutable.new(
|
36
|
-
url: "http://localhost:3000",
|
37
|
-
upload_types: ["Upload"], # << extract `Upload` scalars into multipart forms
|
38
|
-
),
|
39
|
-
},
|
40
|
-
bravo: {
|
41
|
-
schema: GraphQL::Schema.from_definition(...),
|
42
|
-
executable: GraphQL::Stitching::HttpExecutable.new(
|
43
|
-
url: "http://localhost:3001"
|
44
|
-
),
|
45
|
-
},
|
46
|
-
})
|
47
|
-
```
|
48
|
-
|
49
|
-
A location's `HttpExecutable` can then re-package `Tempfile` variables into multipart forms before sending them upstream. This is enabled with an `upload_types` parameter that specifies which scalar names require form extraction. Enabling `upload_types` does add some additional subgraph request processing, so it should only be enabled for locations that will actually recieve file uploads.
|
50
|
-
|
51
|
-
The upstream location will recieve a multipart form submission from stitching that can again be unpacked using [apollo_upload_server](https://github.com/jetruby/apollo_upload_server-ruby) or similar.
|
data/docs/mechanics.md
DELETED
@@ -1,306 +0,0 @@
|
|
1
|
-
## Schema Stitching, mechanics
|
2
|
-
|
3
|
-
### Deploying a stitched schema
|
4
|
-
|
5
|
-
Among the simplest and most effective ways to manage a stitched schema is to compose it locally, write the composed SDL as a `.graphql` file in your repo, and then load the composed schema into a stitching client at runtime. For example, setup a `rake` task that loads/fetches subgraph schemas, composes them, and then writes the composed schema definition as a file committed to the repo:
|
6
|
-
|
7
|
-
```ruby
|
8
|
-
task :compose_graphql do
|
9
|
-
schema1_sdl = ... # load schema 1
|
10
|
-
schema2_sdl = ... # load schema 2
|
11
|
-
|
12
|
-
supergraph = GraphQL::Stitching::Composer.new.perform({
|
13
|
-
schema1: {
|
14
|
-
schema: GraphQL::Schema.from_definition(schema1_sdl)
|
15
|
-
},
|
16
|
-
schema2: {
|
17
|
-
schema: GraphQL::Schema.from_definition(schema2_sdl)
|
18
|
-
}
|
19
|
-
})
|
20
|
-
|
21
|
-
File.write("schema/supergraph.graphql", supergraph.to_definition)
|
22
|
-
puts "Schema composition was successful."
|
23
|
-
end
|
24
|
-
|
25
|
-
# bundle exec rake compose-graphql
|
26
|
-
```
|
27
|
-
|
28
|
-
Then at runtime, load the composed schema into a stitching client:
|
29
|
-
|
30
|
-
```ruby
|
31
|
-
class GraphQlController
|
32
|
-
class < self
|
33
|
-
def client
|
34
|
-
@client ||= begin
|
35
|
-
supergraph_sdl = File.read("schema/supergraph.graphql")
|
36
|
-
supergraph = GraphQL::Stitching::Supergraph.from_definition(supergraph_sdl, executables: {
|
37
|
-
schema1: GraphQL::Stitching::HttpExecutable.new("http://localhost:3001/graphql"),
|
38
|
-
schema2: GraphQL::Stitching::HttpExecutable.new("http://localhost:3002/graphql"),
|
39
|
-
})
|
40
|
-
GraphQL::Stitching::Client.new(supergraph: supergraph)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def exec
|
46
|
-
self.class.client.execute(
|
47
|
-
params[:query],
|
48
|
-
variables: params[:variables],
|
49
|
-
operation_name: params[:operation_name]
|
50
|
-
)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
```
|
54
|
-
|
55
|
-
This process assures that composition always happens before deployment where failures can be detected. Use CI to verify that the repo's supergraph output is always up to date. Hot reloading of the supergraph can also be accommodated by uploading the composed schema to a sync location (cloud storage, etc) that is polled by the application runtime. When the schema changes, load it into a new stitching client and swap that into the application.
|
56
|
-
|
57
|
-
### Field selection routing
|
58
|
-
|
59
|
-
Fields of a merged type may exist in multiple locations. For example, the `title` field below is provided by both locations:
|
60
|
-
|
61
|
-
```graphql
|
62
|
-
# -- Location A
|
63
|
-
|
64
|
-
type Movie {
|
65
|
-
id: String!
|
66
|
-
title: String! # shared
|
67
|
-
rating: Int!
|
68
|
-
}
|
69
|
-
|
70
|
-
type Query {
|
71
|
-
movieA(id: ID!): Movie @stitch(key: "id")
|
72
|
-
}
|
73
|
-
|
74
|
-
# -- Location B
|
75
|
-
|
76
|
-
type Movie {
|
77
|
-
id: String!
|
78
|
-
title: String! # shared
|
79
|
-
reviews: [String!]!
|
80
|
-
}
|
81
|
-
|
82
|
-
type Query {
|
83
|
-
movieB(id: ID!): Movie @stitch(key: "id")
|
84
|
-
}
|
85
|
-
```
|
86
|
-
|
87
|
-
When planning a request, field selections always attempt to use the current routing location that originates from the selection root, for example:
|
88
|
-
|
89
|
-
```graphql
|
90
|
-
query GetTitleFromA {
|
91
|
-
movieA(id: "23") { # <- enter via Location A
|
92
|
-
title # <- source from Location A
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
query GetTitleFromB {
|
97
|
-
movieB(id: "23") { # <- enter via Location B
|
98
|
-
title # <- source from Location B
|
99
|
-
}
|
100
|
-
}
|
101
|
-
```
|
102
|
-
|
103
|
-
Field selections that are NOT available in the current routing location delegate to new locations as follows:
|
104
|
-
|
105
|
-
1. Fields with only one location automatically use that location.
|
106
|
-
2. Fields with multiple locations attempt to use a location added during step-1.
|
107
|
-
3. Any remaining fields pick a location based on their highest availability among locations.
|
108
|
-
|
109
|
-
### Root selection routing
|
110
|
-
|
111
|
-
Root fields should route to the primary locations of their provided types. This assures that the most common data for a type can be resolved via root access and thus avoid unnecessary stitching. Root fields can select their primary locations using the `root_field_location_selector` option in [composer configuration](./composer.md#configuring-composition):
|
112
|
-
|
113
|
-
```ruby
|
114
|
-
supergraph = GraphQL::Stitching::Composer.new(
|
115
|
-
root_field_location_selector: ->(locations) { locations.find { _1 == "a" } || locations.last },
|
116
|
-
).perform({ ... })
|
117
|
-
```
|
118
|
-
|
119
|
-
It's okay if root field names are repeated across locations. The primary location will be used when routing root selections:
|
120
|
-
|
121
|
-
```graphql
|
122
|
-
# -- Location A
|
123
|
-
|
124
|
-
type Movie {
|
125
|
-
id: String!
|
126
|
-
rating: Int!
|
127
|
-
}
|
128
|
-
|
129
|
-
type Query {
|
130
|
-
movie(id: ID!): Movie @stitch(key: "id") # shared, primary
|
131
|
-
}
|
132
|
-
|
133
|
-
# -- Location B
|
134
|
-
|
135
|
-
type Movie {
|
136
|
-
id: String!
|
137
|
-
reviews: [String!]!
|
138
|
-
}
|
139
|
-
|
140
|
-
type Query {
|
141
|
-
movie(id: ID!): Movie @stitch(key: "id") # shared
|
142
|
-
}
|
143
|
-
|
144
|
-
# -- Request
|
145
|
-
|
146
|
-
query {
|
147
|
-
movie(id: "23") { id } # routes to Location A
|
148
|
-
}
|
149
|
-
```
|
150
|
-
|
151
|
-
Note that primary location routing _only_ applies to selections in the root scope. If the `Query` type appears again lower in the graph, then its fields are resolved as normal object fields outside of root context, for example:
|
152
|
-
|
153
|
-
```graphql
|
154
|
-
schema {
|
155
|
-
query: Query # << root query, uses primary locations
|
156
|
-
}
|
157
|
-
|
158
|
-
type Query {
|
159
|
-
subquery: Query # << subquery, acts as a normal object type
|
160
|
-
}
|
161
|
-
```
|
162
|
-
|
163
|
-
Also note that stitching queries (denoted by the `@stitch` directive) are completely separate from field routing concerns. A `@stitch` directive establishes a contract for resolving a given type in a given location. This contract is always used to collect stitching data, regardless of how request routing selected the location for use.
|
164
|
-
|
165
|
-
### Stitched errors
|
166
|
-
|
167
|
-
Any [spec GraphQL errors](https://spec.graphql.org/June2018/#sec-Errors) returned by a stitching query will flow through the request. Stitching has two strategies for passing errors through to the final result:
|
168
|
-
|
169
|
-
1. **Direct passthrough**, where subgraph errors are returned directly without modification. This strategy is used for errors without a `path` (ie: "base" errors), and errors pathed to root fields.
|
170
|
-
|
171
|
-
2. **Mapped passthrough**, where the `path` attribute of a subgraph error is remapped to an insertion point in the stitched request. This strategy is used when merging stitching queries into the composed result.
|
172
|
-
|
173
|
-
In either strategy, it's important that subgraphs provide properly pathed errors (GraphQL Ruby [can do this automatically](https://graphql-ruby.org/errors/overview.html)). For example:
|
174
|
-
|
175
|
-
```json
|
176
|
-
{
|
177
|
-
"data": { "shop": { "product": null } },
|
178
|
-
"errors": [{
|
179
|
-
"message": "Record not found.",
|
180
|
-
"path": ["shop", "product"]
|
181
|
-
}]
|
182
|
-
}
|
183
|
-
```
|
184
|
-
|
185
|
-
When resolving [stitching list queries](../README.md#list-queries), it's important to only error out specific array positions rather than the entire array result, for example:
|
186
|
-
|
187
|
-
```ruby
|
188
|
-
def products
|
189
|
-
[
|
190
|
-
{ id: "1" },
|
191
|
-
GraphQL::ExecutionError.new("Not found"),
|
192
|
-
{ id: "3" },
|
193
|
-
]
|
194
|
-
end
|
195
|
-
```
|
196
|
-
|
197
|
-
Stitching expects list queries to pad their missing elements with null, and to report corresponding errors pathed down to list position:
|
198
|
-
|
199
|
-
```json
|
200
|
-
{
|
201
|
-
"data": {
|
202
|
-
"products": [{ "id": "1" }, null, { "id": "3" }]
|
203
|
-
},
|
204
|
-
"errors": [{
|
205
|
-
"message": "Record not found.",
|
206
|
-
"path": ["products", 1]
|
207
|
-
}]
|
208
|
-
}
|
209
|
-
```
|
210
|
-
|
211
|
-
### Null results
|
212
|
-
|
213
|
-
It's okay for a stitching query to return `null` for a merged type as long as all unique fields of the type allow null. For example, the following merge works:
|
214
|
-
|
215
|
-
```graphql
|
216
|
-
# -- Request
|
217
|
-
|
218
|
-
query {
|
219
|
-
movieA(id: "23") {
|
220
|
-
id
|
221
|
-
title
|
222
|
-
rating
|
223
|
-
}
|
224
|
-
}
|
225
|
-
|
226
|
-
# -- Location A
|
227
|
-
|
228
|
-
type Movie {
|
229
|
-
id: String!
|
230
|
-
title: String!
|
231
|
-
}
|
232
|
-
|
233
|
-
type Query {
|
234
|
-
movieA(id: ID!): Movie @stitch(key: "id")
|
235
|
-
# (id: "23") -> { id: "23", title: "Jurassic Park" }
|
236
|
-
}
|
237
|
-
|
238
|
-
# -- Location B
|
239
|
-
|
240
|
-
type Movie {
|
241
|
-
id: String!
|
242
|
-
rating: Int
|
243
|
-
}
|
244
|
-
|
245
|
-
type Query {
|
246
|
-
movieB(id: ID!): Movie @stitch(key: "id")
|
247
|
-
# (id: "23") -> null
|
248
|
-
}
|
249
|
-
```
|
250
|
-
|
251
|
-
And produces this result:
|
252
|
-
|
253
|
-
```json
|
254
|
-
{
|
255
|
-
"data": {
|
256
|
-
"id": "23",
|
257
|
-
"title": "Jurassic Park",
|
258
|
-
"rating": null
|
259
|
-
}
|
260
|
-
}
|
261
|
-
```
|
262
|
-
|
263
|
-
Location B is allowed to return `null` here because its one unique field, `rating`, is nullable (the `id` field can be provided by Location A). If `rating` were non-null, then null bubbling would invalidate the response data.
|
264
|
-
|
265
|
-
### Outbound-only merged types
|
266
|
-
|
267
|
-
Merged types do not always require a resolver query. For example:
|
268
|
-
|
269
|
-
```graphql
|
270
|
-
# -- Location A
|
271
|
-
|
272
|
-
type Widget {
|
273
|
-
id: ID!
|
274
|
-
name: String
|
275
|
-
price: Float
|
276
|
-
}
|
277
|
-
|
278
|
-
type Query {
|
279
|
-
widgetA(id: ID!): Widget @stitch(key: "id")
|
280
|
-
}
|
281
|
-
|
282
|
-
# -- Location B
|
283
|
-
|
284
|
-
type Widget {
|
285
|
-
id: ID!
|
286
|
-
size: Float
|
287
|
-
}
|
288
|
-
|
289
|
-
type Query {
|
290
|
-
widgetB(id: ID!): Widget @stitch(key: "id")
|
291
|
-
}
|
292
|
-
|
293
|
-
# -- Location C
|
294
|
-
|
295
|
-
type Widget {
|
296
|
-
id: ID!
|
297
|
-
name: String
|
298
|
-
size: Float
|
299
|
-
}
|
300
|
-
|
301
|
-
type Query {
|
302
|
-
featuredWidget: Widget
|
303
|
-
}
|
304
|
-
```
|
305
|
-
|
306
|
-
In this graph, `Widget` is a merged type without a resolver query in location C. This works because all of its fields are resolvable in other locations; that means location C can provide outbound representations of this type without ever needing to resolve inbound requests for it. Outbound types do still require a shared key field (such as `id` above) that allow them to join with data in other resolver locations (such as `price` above). Support for this pattern is limited to single-field keys, [composite keys](../README.md#composite-type-keys) require a resolver definition.
|
data/docs/request.md
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
## GraphQL::Stitching::Request
|
2
|
-
|
3
|
-
A `Request` contains a parsed GraphQL document and variables, and handles the logistics of extracting the appropriate operation, variable definitions, and fragments. A `Request` should be built once per server request and passed through to other stitching components that utilize request information.
|
4
|
-
|
5
|
-
```ruby
|
6
|
-
source = "query FetchMovie($id: ID!) { movie(id:$id) { id genre } }"
|
7
|
-
request = GraphQL::Stitching::Request.new(
|
8
|
-
supergraph,
|
9
|
-
source,
|
10
|
-
variables: { "id" => "1" },
|
11
|
-
operation_name: "FetchMovie",
|
12
|
-
context: { ... },
|
13
|
-
)
|
14
|
-
```
|
15
|
-
|
16
|
-
A `Request` provides the following information:
|
17
|
-
|
18
|
-
- `req.variables`: a hash of user-submitted variables.
|
19
|
-
- `req.string`: the original GraphQL source string, or printed document.
|
20
|
-
- `req.digest`: a digest of the request string, hashed by the `Stitching.digest` implementation.
|
21
|
-
- `req.normalized_string`: printed document string with consistent whitespace.
|
22
|
-
- `req.normalized_digest`: a digest of the normalized string, hashed by the `Stitching.digest` implementation.
|
23
|
-
- `req.operation`: the operation definition selected for the request.
|
24
|
-
- `req.variable_definitions`: a mapping of variable names to their type definitions.
|
25
|
-
- `req.fragment_definitions`: a mapping of fragment names to their fragment definitions.
|
26
|
-
|
27
|
-
### Request lifecycle
|
28
|
-
|
29
|
-
A request manages the flow of stitching behaviors. These are sequenced by the `Client`
|
30
|
-
component, or you may invoke them manually:
|
31
|
-
|
32
|
-
1. `request.validate`: runs static validations on the request using the combined schema.
|
33
|
-
2. `request.plan`: builds a plan for the request. May act as a setter for plans pulled from cache.
|
34
|
-
3. `request.execute`: executes the request, and returns the resulting data.
|
data/docs/supergraph.md
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
## GraphQL::Stitching::Supergraph
|
2
|
-
|
3
|
-
A `Supergraph` is the singuar representation of a stitched graph. `Supergraph` is composed from many locations, and provides a combined GraphQL schema and delegation maps used to route incoming requests.
|
4
|
-
|
5
|
-
### Export and caching
|
6
|
-
|
7
|
-
A Supergraph is designed to be composed, cached, and restored. Calling `to_definition` will return an SDL (Schema Definition Language) print of the combined graph schema with delegation mapping directives. This pre-composed schema can be persisted in any raw format that suits your stack:
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
supergraph_sdl = supergraph.to_definition
|
11
|
-
|
12
|
-
# write the composed schema as a file into your repo...
|
13
|
-
File.write("supergraph/schema.graphql", supergraph_sdl)
|
14
|
-
|
15
|
-
# or, stash this composed schema in a cache...
|
16
|
-
$cache.set("cached_supergraph_sdl", supergraph_sdl)
|
17
|
-
```
|
18
|
-
|
19
|
-
To restore a Supergraph, call `from_definition` providing the cached SDL string and a hash of executables keyed by their location names:
|
20
|
-
|
21
|
-
```ruby
|
22
|
-
supergraph_sdl = File.read("supergraph/schema.graphql")
|
23
|
-
|
24
|
-
supergraph = GraphQL::Stitching::Supergraph.from_definition(
|
25
|
-
supergraph_sdl,
|
26
|
-
executables: {
|
27
|
-
my_remote: GraphQL::Stitching::HttpExecutable.new(url: "http://localhost:3000"),
|
28
|
-
my_local: MyLocalSchema,
|
29
|
-
}
|
30
|
-
)
|
31
|
-
```
|
data/docs/type_resolver.md
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
## GraphQL::Stitching::TypeResolver
|
2
|
-
|
3
|
-
A `TypeResolver` contains all information about a root query used by stitching to fetch location-specific variants of a merged type. Specifically, resolvers manage parsed keys and argument structures.
|
4
|
-
|
5
|
-
### Arguments
|
6
|
-
|
7
|
-
Type resolvers configure arguments through a template string of [GraphQL argument literal syntax](https://spec.graphql.org/October2021/#sec-Language.Arguments). This allows sending multiple arguments that intermix stitching keys with complex object shapes and other static values.
|
8
|
-
|
9
|
-
#### Key insertions
|
10
|
-
|
11
|
-
Key values fetched from previous locations may be inserted into arguments. Key insertions are prefixed by `$` and specify a dot-notation path to any selections made by the resolver `key`, or `__typename`.
|
12
|
-
|
13
|
-
```graphql
|
14
|
-
type Query {
|
15
|
-
entity(id: ID!, type: String!): [Entity]!
|
16
|
-
@stitch(key: "owner { id }", arguments: "id: $.owner.id, type: $.__typename")
|
17
|
-
}
|
18
|
-
```
|
19
|
-
|
20
|
-
Key insertions are _not_ quoted to differentiate them from other literal values.
|
21
|
-
|
22
|
-
#### Lists
|
23
|
-
|
24
|
-
List arguments may specify input just like non-list arguments, and [GraphQL list input coercion](https://spec.graphql.org/October2021/#sec-List.Input-Coercion) will assume the shape represents a list item:
|
25
|
-
|
26
|
-
```graphql
|
27
|
-
type Query {
|
28
|
-
product(ids: [ID!]!, source: DataSource!): [Product]!
|
29
|
-
@stitch(key: "id", arguments: "ids: $.id, source: CACHE")
|
30
|
-
}
|
31
|
-
```
|
32
|
-
|
33
|
-
List resolvers (that return list types) may _only_ insert keys into repeatable list arguments, while non-list arguments may only contain static values. Nested list inputs are neither common nor practical, so are not supported.
|
34
|
-
|
35
|
-
#### Built-in scalars
|
36
|
-
|
37
|
-
Built-in scalars are written as normal literal values. For convenience, string literals may be enclosed in single quotes rather than escaped double-quotes:
|
38
|
-
|
39
|
-
```graphql
|
40
|
-
type Query {
|
41
|
-
product(id: ID!, source: String!): Product
|
42
|
-
@stitch(key: "id", arguments: "id: $.id, source: 'cache'")
|
43
|
-
|
44
|
-
variant(id: ID!, limit: Int!): Variant
|
45
|
-
@stitch(key: "id", arguments: "id: $.id, limit: 100")
|
46
|
-
}
|
47
|
-
```
|
48
|
-
|
49
|
-
All scalar usage must be legal to the resolver field's arguments schema.
|
50
|
-
|
51
|
-
#### Enums
|
52
|
-
|
53
|
-
Enum literals may be provided anywhere in the input structure. They are _not_ quoted:
|
54
|
-
|
55
|
-
```graphql
|
56
|
-
enum DataSource {
|
57
|
-
CACHE
|
58
|
-
}
|
59
|
-
type Query {
|
60
|
-
product(id: ID!, source: DataSource!): [Product]!
|
61
|
-
@stitch(key: "id", arguments: "id: $.id, source: CACHE")
|
62
|
-
}
|
63
|
-
```
|
64
|
-
|
65
|
-
All enum usage must be legal to the resolver field's arguments schema.
|
66
|
-
|
67
|
-
#### Input Objects
|
68
|
-
|
69
|
-
Input objects may be provided anywhere in the input, even as nested structures. The stitching resolver will build the specified object shape:
|
70
|
-
|
71
|
-
```graphql
|
72
|
-
input ComplexKey {
|
73
|
-
id: ID
|
74
|
-
nested: ComplexKey
|
75
|
-
}
|
76
|
-
type Query {
|
77
|
-
product(key: ComplexKey!): [Product]!
|
78
|
-
@stitch(key: "id", arguments: "key: { nested: { id: $.id } }")
|
79
|
-
}
|
80
|
-
```
|
81
|
-
|
82
|
-
Input object shapes must conform to their respective schema definitions based on their placement within resolver arguments.
|
83
|
-
|
84
|
-
#### Custom scalars
|
85
|
-
|
86
|
-
Custom scalar keys allow any input shape to be submitted, from primitive scalars to complex object structures. These values will be sent and recieved as untyped JSON input:
|
87
|
-
|
88
|
-
```graphql
|
89
|
-
type Product {
|
90
|
-
id: ID!
|
91
|
-
}
|
92
|
-
union Entity = Product
|
93
|
-
scalar Key
|
94
|
-
|
95
|
-
type Query {
|
96
|
-
entities(representations: [Key!]!): [Entity]!
|
97
|
-
@stitch(key: "id", arguments: "representations: { id: $.id, __typename: $.__typename }")
|
98
|
-
}
|
99
|
-
```
|
100
|
-
|
101
|
-
Custom scalar arguments have no structured schema definition to validate against. This makes them flexible but quite lax, for better or worse.
|