Skip to content

Commit f59bc9b

Browse files
authored
docs: update v6 docs with fed2.3 info (#1686)
1 parent a866996 commit f59bc9b

File tree

2 files changed

+183
-65
lines changed

2 files changed

+183
-65
lines changed

website/versioned_docs/version-6.x.x/schema-generator/federation/apollo-federation.mdx

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,34 +117,43 @@ toFederatedSchema(
117117
will generate
118118

119119
```graphql
120-
# Federation spec types
121-
scalar _Any
122-
scalar FieldSet
123-
124-
union _Entity
125-
126-
type _Service {
127-
sdl: String!
120+
schema @link(import : ["@composeDirective", "@extends", "@external", "@inaccessible", "@interfaceObject", "@key", "@override", "@provides", "@requires", "@shareable", "@tag", "FieldSet"], url : "https://specs.apollo.dev/federation/v2.3"){
121+
query: Query
128122
}
129123

124+
directive @composeDirective(name: String!) repeatable on SCHEMA
125+
directive @extends on OBJECT | INTERFACE
130126
directive @external on FIELD_DEFINITION
131-
directive @requires(fields: FieldSet) on FIELD_DEFINITION
132-
directive @provides(fields: FieldSet) on FIELD_DEFINITION
127+
directive @inaccessible on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
128+
directive @interfaceObject on OBJECT
133129
directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
134-
directive @extends on OBJECT | INTERFACE
135-
136-
# Schema types
137-
type Query @extends {
130+
directive @link(import: [String], url: String!) repeatable on SCHEMA
131+
directive @override(from: String!) on FIELD_DEFINITION
132+
directive @provides(fields: FieldSet!) on FIELD_DEFINITION
133+
directive @requires(fields: FieldSet!) on FIELD_DEFINITION
134+
directive @shareable on OBJECT | FIELD_DEFINITION
135+
directive @tag(name: String!) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
136+
137+
type Query {
138138
getUsers: [User!]!
139139

140140
_entities(representations: [_Any!]!): [_Entity]!
141141
_service: _Service!
142142
}
143143

144-
type User @key(fields : "id") {
144+
type User @key(fields : "id", resolvable : true) {
145145
id: ID!
146146
name: String!
147147
}
148+
149+
union _Entity = User
150+
151+
type _Service {
152+
sdl: String!
153+
}
154+
155+
scalar FieldSet
156+
scalar _Any
148157
```
149158

150159
## Limitations

website/versioned_docs/version-6.x.x/schema-generator/federation/federated-directives.md

Lines changed: 159 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,49 @@ title: Federated Directives
66

77
For more details, see the [Apollo Federation Specification](https://www.apollographql.com/docs/federation/federation-spec/).
88

9+
## `@composeDirective` directive
10+
11+
```graphql
12+
directive @composeDirective(name: String!) repeatable on SCHEMA
13+
```
14+
15+
By default, Supergraph schema excludes all custom directives. The `@composeDirective` is used to specify custom directives that should be exposed in the Supergraph schema.
16+
17+
Example:
18+
Given `@custom` directive we can preserve it in the Supergraph schema
19+
20+
```kotlin
21+
@GraphQLDirective(name = "custom", locations = [Introspection.DirectiveLocation.FIELD_DEFINITION])
22+
annotation class CustomDirective
23+
24+
@ComposeDirective(name = "custom")
25+
class CustomSchema
26+
27+
class SimpleQuery {
28+
@CustomDirective
29+
fun helloWorld(): String = "Hello World"
30+
}
31+
```
32+
33+
it will generate following schema
34+
35+
```graphql
36+
schema
37+
@composeDirective(name: "@myDirective")
38+
@link(import : ["@composeDirective", "@extends", "@external", "@inaccessible", "@interfaceObject", "@key", "@override", "@provides", "@requires", "@shareable", "@tag", "FieldSet"], url : "https://specs.apollo.dev/federation/v2.3")
39+
{
40+
query: Query
41+
}
42+
43+
directive @custom on FIELD_DEFINITION
44+
45+
type Query {
46+
helloWorld: String! @custom
47+
}
48+
```
49+
50+
See [@composeDirective definition](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#composedirective) for more information.
51+
952
## `@contact` directive
1053

1154
```graphql
@@ -78,32 +121,40 @@ type Product @key(fields : "id") @extends {
78121
## `@external` directive
79122

80123
```graphql
124+
# federation v1 definition
81125
directive @external on FIELD_DEFINITION
126+
127+
# federation v2 definition
128+
directive @external on OBJECT | FIELD_DEFINITION
82129
```
83130

84131
The `@external` directive is used to mark a field as owned by another service. This allows service A to use fields from
85-
service B while also knowing at runtime the types of that field. `@external` directive is only applicable on federated
86-
extended types. All the external fields should either be referenced from the `@key`, `@requires` or `@provides`
87-
directives field sets.
132+
service B while also knowing at runtime the types of that field. All the external fields should either be referenced from
133+
the `@key`, `@requires` or `@provides` directives field sets.
88134

89-
Due to the smart merging of entity types, `@external` directive is no longer required on `@key` fields and can be omitted
90-
from the schema. `@external` directive is only required on fields referenced by the `@requires` and `@provides` directive.
135+
Due to the smart merging of entity types, Federation v2 no longer requires `@external` directive on `@key` fields and can
136+
be safely omitted from the schema. `@external` directive is only required on fields referenced by the `@requires` and
137+
`@provides` directive.
91138

92139
#### Example
93140

94141
```kotlin
95142
@KeyDirective(FieldSet("id"))
96-
@ExtendsDirective
97-
class Product(@ExternalDirective val id: String) {
98-
fun newFunctionality(): String = "whatever"
143+
class Product(val id: String) {
144+
@ExternalDirective
145+
var externalField: String by Delegates.notNull()
146+
147+
@RequiresDirective(FieldSet("externalField"))
148+
fun newFunctionality(): String { ... }
99149
}
100150
```
101151

102152
will generate
103153

104154
```graphql
105-
type Product @key(fields : "id") @extends {
106-
id: String! @external
155+
type Product @key(fields : "id") {
156+
externalField: String! @external
157+
id: String!
107158
newFunctionality: String!
108159
}
109160
```
@@ -127,7 +178,10 @@ directive @inaccessible on FIELD_DEFINITION
127178
| ARGUMENT_DEFINITION
128179
```
129180

130-
Inaccessible directive marks location within schema as inaccessible from the GraphQL Gateway. This allows you to incrementally add schema elements (e.g. fields) to multiple subgraphs without breaking composition.
181+
Inaccessible directive marks location within schema as inaccessible from the GraphQL Gateway. While `@inaccessible` fields are not exposed by the gateway to the clients,
182+
they are still available for query plans and can be referenced from `@key` and `@requires` directives. This allows you to not expose sensitive fields to your clients but
183+
still make them available for computations. Inaccessible can also be used to incrementally add schema elements (e.g. fields) to multiple subgraphs without breaking composition.
184+
131185
See [@inaccessible specification](https://specs.apollo.dev/inaccessible/v0.2) for additional details.
132186

133187
:::caution
@@ -161,6 +215,60 @@ type Product {
161215
}
162216
```
163217

218+
## `@interfaceObject` directive
219+
220+
:::note
221+
Only available in Federation v2.
222+
:::
223+
224+
```graphql
225+
directive @interfaceObject on OBJECT
226+
```
227+
228+
This directive provides meta information to the router that this entity type defined within this subgraph is an interface in the supergraph. This allows you to extend functionality
229+
of an interface across the supergraph without having to implement (or even be aware of) all its implementing types.
230+
231+
Example:
232+
Given an interface that is defined somewhere in our supergraph
233+
234+
```graphql
235+
interface Product @key(fields: "id") {
236+
id: ID!
237+
description: String
238+
}
239+
240+
type Book implements Product @key(fields: "id") {
241+
id: ID!
242+
description: String
243+
pages: Int!
244+
}
245+
246+
type Movie implements Product @key(fields: "id") {
247+
id: ID!
248+
description: String
249+
duration: Int!
250+
}
251+
```
252+
253+
We can extend `Product` entity in our subgraph and a new field directly to it. This will result in making this new field available to ALL implementing types.
254+
255+
```kotlin
256+
@InterfaceObjectDirective
257+
@KeyDirective(fields = FieldSet("id"))
258+
data class Product(val id: ID) {
259+
fun reviews(): List<Review> = TODO()
260+
}
261+
```
262+
263+
Which generates the following subgraph schema
264+
265+
```graphql
266+
type Product @key(fields: "id") @interfaceObject {
267+
id: ID!
268+
reviews: [Review!]!
269+
}
270+
```
271+
164272
## `@key` directive
165273

166274
```graphql
@@ -175,10 +283,9 @@ The `@key` directive is used to indicate a combination of fields that can be use
175283
object or interface. The specified field set can represent single field (e.g. `"id"`), multiple fields (e.g. `"id name"`) or
176284
nested selection sets (e.g. `"id user { name }"`). Multiple keys can be specified on a target type.
177285

178-
Key directives should be specified on the root base type as well as all the corresponding federated (i.e. extended)
179-
types. Key fields specified in the directive field set should correspond to a valid field on the underlying GraphQL
180-
interface/object. Federated extended types should also instrument all the referenced key fields with `@external`
181-
directive.
286+
Key directives should be specified on all entities (objects that can resolve its fields across multiple subgraphs). Key
287+
fields specified in the directive field set should correspond to a valid field on the underlying GraphQL interface/object.
288+
Federated extended types should also instrument all the referenced key fields with `@external` directive.
182289

183290
#### Basic Example
184291

@@ -236,7 +343,7 @@ Only available in Federation v2.
236343
:::
237344

238345
```graphql
239-
directive @link(url: String, import: [String!]) repeatable on SCHEMA
346+
directive @link(url: String!, import: [String]) repeatable on SCHEMA
240347
```
241348

242349
The `@link` directive links definitions within the document to external schemas. See [@link specification](https://specs.apollo.dev/link/v1.0) for details.
@@ -246,6 +353,19 @@ External schemas are identified by their `url`, which optionally ends with a nam
246353
By default, external types should be namespaced (prefixed with `<namespace>__`, e.g. `key` directive should be namespaced as `federation__key`) unless they are explicitly imported.
247354
`graphql-kotlin` automatically imports ALL federation directives to avoid the need for namespacing.
248355

356+
```kotlin
357+
@LinkDirective(url = "https://myspecs.company.dev/foo/v1.0", imports = ["@foo", "bar"])
358+
class MySchema
359+
```
360+
361+
This will generate following schema:
362+
363+
```graphql
364+
schema @link(import : ["@foo", "bar"], url : "https://myspecs.company.dev/foo/v1.0") {
365+
query: Query
366+
}
367+
```
368+
249369
:::danger
250370
We currently DO NOT support full `@link` directive capability as it requires support for namespacing and renaming imports. This functionality may be added in the future releases. See
251371
[@link specification](https://specs.apollo.dev/link/v1.0) for details.
@@ -300,18 +420,16 @@ directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
300420
directive @provides(fields: FieldSet!) on FIELD_DEFINITION
301421
```
302422

303-
The `@provides` directive is used to annotate the expected returned field set from a field on a base type that is
304-
guaranteed to be selectable by the gateway. This allows you to expose only a subset of fields from the underlying
305-
federated object type to be selectable from the federated schema. Provided fields specified in the directive field set
306-
should correspond to a valid field on the underlying GraphQL interface/object type. `@provides` directive can only be
307-
used on fields returning federated extended objects.
308-
309-
#### Example 1:
423+
The `@provides` directive is a router optimization hint specifying field set that can be resolved locally at the given subgraph through this particular query path. This allows you to
424+
expose only a subset of fields from the underlying entity type to be selectable from the federated schema without the need to call other subgraphs. Provided fields specified in the
425+
directive field set should correspond to a valid field on the underlying GraphQL interface/object type. `@provides` directive can only be used on fields returning entities.
310426

311427
:::info
312-
Due to the smart entity type merging, Federation v2 does not require `@provides` directive if field can always be resolved locally.
428+
Federation v2 does not require `@provides` directive if field can **always** be resolved locally. `@provides` should be omitted in this situation.
313429
:::
314430

431+
#### Example 1:
432+
315433
We might want to expose only name of the user that submitted a review.
316434

317435
```kotlin
@@ -322,9 +440,8 @@ class Review(val id: String) {
322440
}
323441

324442
@KeyDirective(FieldSet("userId"))
325-
@ExtendsDirective
326443
class User(
327-
@ExternalDirective val userId: String,
444+
val userId: String,
328445
@ExternalDirective val name: String
329446
)
330447
```
@@ -337,8 +454,8 @@ type Review @key(fields : "id") {
337454
user: User! @provides(fields : "name")
338455
}
339456

340-
type User @key(fields : "userId") @extends {
341-
userId: String! @external
457+
type User @key(fields : "userId") {
458+
userId: String!
342459
name: String! @external
343460
}
344461
```
@@ -372,43 +489,34 @@ directive @requires(fields: _FieldSet!) on FIELD_DEFINITON
372489
directive @requires(fields: FieldSet!) on FIELD_DEFINITON
373490
```
374491

375-
The `@requires` directive is used to annotate the required input field set from a base type for a resolver. It is used
376-
to develop a query plan where the required fields may not be needed by the client, but the service may need additional
377-
information from other services. Required fields specified in the directive field set should correspond to a valid field
378-
on the underlying GraphQL interface/object and should be instrumented with `@external` directive. Since `@requires`
379-
directive specifies additional fields (besides the one specified in `@key` directive) that are required to resolve
380-
federated type fields, this directive can only be specified on federated extended objects fields.
492+
The `@requires` directive is used to specify external (provided by other subgraphs) entity fields that are needed to resolve target field. It is used to develop a query plan where
493+
the required fields may not be needed by the client, but the service may need additional information from other subgraphs. Required fields specified in the directive field set should
494+
correspond to a valid field on the underlying GraphQL interface/object and should be instrumented with `@external` directive.
381495

382-
Fields specified in the `@requires` directive will only be specified in the queries that reference those fields.
383-
This is problematic for Kotlin as the non nullable primitive properties have to be initialized when they are declared.
384-
Simplest workaround for this problem is to initialize the underlying property to some dummy value that will be used if
385-
it is not specified. This approach might become problematic though as it might be impossible to determine whether fields
386-
was initialized with the default value or the invalid/default value was provided by the federated query. Another
387-
potential workaround is to rely on delegation to initialize the property after the object gets created. This will ensure
388-
that exception will be thrown if queries attempt to resolve fields that reference the uninitialized property.
496+
Fields specified in the `@requires` directive will only be specified in the queries that reference those fields. This is problematic for Kotlin as the non-nullable primitive properties
497+
have to be initialized when they are declared. Simplest workaround for this problem is to initialize the underlying property to some default value (e.g. null) that will be used if
498+
it is not specified. This approach might become problematic though as it might be impossible to determine whether fields was initialized with the default value or the invalid/default
499+
value was provided by the federated query. Another potential workaround is to rely on delegation to initialize the property after the object gets created. This will ensure that exception
500+
will be thrown if queries attempt to resolve fields that reference the uninitialized property.
389501

390502
#### Example
391503

392504
```kotlin
393505
@KeyDirective(FieldSet("id"))
394-
@ExtendsDirective
395-
class Product(@ExternalDirective val id: String) {
506+
class Product(val id: String) {
396507
@ExternalDirective
397508
var weight: Double by Delegates.notNull()
398509

399510
@RequiresDirective(FieldSet("weight"))
400511
fun shippingCost(): String { ... }
401-
402-
fun additionalInfo(): String { ... }
403512
}
404513
```
405514

406515
will generate
407516

408517
```graphql
409-
type Product @key(fields : "id") @extends {
410-
additionalInfo: String!
411-
id: String! @external
518+
type Product @key(fields : "id") {
519+
id: String!
412520
shippingCost: String! @requires(fields : "weight")
413521
weight: Float! @external
414522
}
@@ -421,7 +529,7 @@ Only available in Federation v2.
421529
:::
422530

423531
```graphql
424-
directive @shareable on FIELD_DEFINITION | OBJECT
532+
directive @shareable repeatable on FIELD_DEFINITION | OBJECT
425533
```
426534

427535
Shareable directive indicates that given object and/or field can be resolved by multiple subgraphs. If an object is marked as `@shareable` then all its fields are automatically shareable without the
@@ -474,5 +582,6 @@ type Product @tag(name: "MyCustomTag") {
474582
```
475583

476584
:::caution
477-
Apollo Contracts behave slightly differently depending on which version of Apollo Federation your graph uses (1 or 2). See [documentation](https://www.apollographql.com/docs/studio/contracts/#federation-1-limitations) for details.
585+
Apollo Contracts behave slightly differently depending on which version of Apollo Federation your graph uses (1 or 2). See [documentation](https://www.apollographql.com/docs/studio/contracts/#federation-1-limitations)
586+
for details.
478587
:::

0 commit comments

Comments
 (0)