graphql-stitching 1.7.0 → 1.7.2

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.
data/docs/composer.md DELETED
@@ -1,125 +0,0 @@
1
- ## GraphQL::Stitching::Composer
2
-
3
- A `Composer` receives many individual `GraphQL::Schema` instances representing various graph locations and _composes_ them into one combined [`Supergraph`](./supergraph.md) that is validated for integrity.
4
-
5
- ### Configuring composition
6
-
7
- A `Composer` may be constructed with optional settings that tune how it builds a schema:
8
-
9
- ```ruby
10
- composer = GraphQL::Stitching::Composer.new(
11
- query_name: "Query",
12
- mutation_name: "Mutation",
13
- subscription_name: "Subscription",
14
- description_merger: ->(values_by_location, info) { values_by_location.values.join("\n") },
15
- deprecation_merger: ->(values_by_location, info) { values_by_location.values.first },
16
- default_value_merger: ->(values_by_location, info) { values_by_location.values.first },
17
- directive_kwarg_merger: ->(values_by_location, info) { values_by_location.values.last },
18
- root_field_location_selector: ->(locations, info) { locations.last },
19
- )
20
- ```
21
-
22
- Constructor arguments:
23
-
24
- - **`query_name:`** _optional_, the name of the root query type in the composed schema; `Query` by default. The root query types from all location schemas will be merged into this type, regardless of their local names.
25
-
26
- - **`mutation_name:`** _optional_, the name of the root mutation type in the composed schema; `Mutation` by default. The root mutation types from all location schemas will be merged into this type, regardless of their local names.
27
-
28
- - **`subscription_name:`** _optional_, the name of the root subscription type in the composed schema; `Subscription` by default. The root subscription types from all location schemas will be merged into this type, regardless of their local names.
29
-
30
- - **`description_merger:`** _optional_, a [value merger function](#value-merger-functions) for merging element description strings from across locations.
31
-
32
- - **`deprecation_merger:`** _optional_, a [value merger function](#value-merger-functions) for merging element deprecation strings from across locations.
33
-
34
- - **`default_value_merger:`** _optional_, a [value merger function](#value-merger-functions) for merging argument default values from across locations.
35
-
36
- - **`directive_kwarg_merger:`** _optional_, a [value merger function](#value-merger-functions) for merging directive keyword arguments from across locations.
37
-
38
- - **`root_field_location_selector:`** _optional_, selects a default routing location for root fields with multiple locations. Use this to prioritize sending root fields to their primary data sources (only applies while routing the root operation scope). This handler receives an array of possible locations and an info object with field information, and should return the prioritized location. The last location is used by default.
39
-
40
- #### Value merger functions
41
-
42
- Static data values such as element descriptions and directive arguments must also merge across locations. By default, the first non-null value encountered for a given element attribute is used. A value merger function may customize this process by selecting a different value or computing a new one:
43
-
44
- ```ruby
45
- composer = GraphQL::Stitching::Composer.new(
46
- description_merger: ->(values_by_location, info) { values_by_location.values.compact.join("\n") },
47
- )
48
- ```
49
-
50
- A merger function receives `values_by_location` and `info` arguments; these provide possible values keyed by location and info about where in the schema these values were encountered:
51
-
52
- ```ruby
53
- values_by_location = {
54
- "storefronts" => "A fabulous data type.",
55
- "products" => "An excellent data type.",
56
- }
57
-
58
- info = {
59
- type_name: "Product",
60
- # field_name: ...,
61
- # argument_name: ...,
62
- # directive_name: ...,
63
- }
64
- ```
65
-
66
- ### Performing composition
67
-
68
- Construct a `Composer` and call its `perform` method with location settings to compose a supergraph:
69
-
70
- ```ruby
71
- storefronts_sdl = "type Query { ..."
72
- products_sdl = "type Query { ..."
73
-
74
- supergraph = GraphQL::Stitching::Composer.new.perform({
75
- storefronts: {
76
- schema: GraphQL::Schema.from_definition(storefronts_sdl),
77
- executable: GraphQL::Stitching::HttpExecutable.new(url: "http://localhost:3001"),
78
- stitch: [{ field_name: "storefront", key: "id" }],
79
- },
80
- products: {
81
- schema: GraphQL::Schema.from_definition(products_sdl),
82
- executable: GraphQL::Stitching::HttpExecutable.new(url: "http://localhost:3002"),
83
- },
84
- my_local: {
85
- schema: MyLocalSchema,
86
- },
87
- })
88
-
89
- combined_schema = supergraph.schema
90
- ```
91
-
92
- Location settings have top-level keys that specify arbitrary location names, each of which provide:
93
-
94
- - **`schema:`** _required_, provides a `GraphQL::Schema` class for the location. This may be a class-based schema that inherits from `GraphQL::Schema`, or built from SDL (Schema Definition Language) string using `GraphQL::Schema.from_definition` and mapped to a remote location. The provided schema is only used for type reference and does not require any real data resolvers (unless it is also used as the location's executable, see below).
95
-
96
- - **`executable:`** _optional_, provides an executable resource to be called when delegating a request to this location. Executables are `GraphQL::Schema` classes or any object with a `.call(request, source, variables)` method that returns a GraphQL response. Omitting the executable option will use the location's provided `schema` as the executable resource.
97
-
98
- - **`stitch:`** _optional_, an array of configs used to dynamically apply `@stitch` directives to select root fields prior to composing. This is useful when you can't easily render stitching directives into a location's source schema.
99
-
100
- ### Merge patterns
101
-
102
- The strategy used to merge source schemas into the combined schema is based on each element type:
103
-
104
- - Arguments of fields, directives, and `InputObject` types intersect for each parent element across locations (an element's arguments must appear in all locations):
105
- - Arguments must share a value type, and the strictest nullability across locations is used.
106
- - Composition fails if argument intersection would eliminate a non-null argument.
107
-
108
- - `Object` and `Interface` types merge their fields and directives together:
109
- - Common fields across locations must share a value type, and the weakest nullability is used.
110
- - Objects with unique fields across locations must implement [`@stitch` accessors](../README.md#merged-types).
111
- - Shared object types without `@stitch` accessors must contain identical fields.
112
- - Merged interfaces must remain compatible with all underlying implementations.
113
-
114
- - `Enum` types merge their values based on how the enum is used:
115
- - Enums used anywhere as an argument will intersect their values (common values across all locations).
116
- - Enums used exclusively in read contexts will provide a union of values (all values across all locations).
117
-
118
- - `Union` types merge all possible types from across all locations.
119
-
120
- - `Scalar` types are added for all scalar names across all locations.
121
-
122
- - `Directive` definitions are added for all distinct names across locations:
123
- - Stitching directives (both definitions and assignments) are omitted.
124
-
125
- Note that the structure of a composed schema may change based on new schema additions and/or element usage (ie: changing input object arguments in one service may cause the intersection of arguments to change). Therefore, it's highly recommended that you use a [schema comparator](https://github.com/xuorig/graphql-schema_comparator) to flag regressions across composed schema versions.
@@ -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
- ```
@@ -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.