0
# Apollo Federation
1
2
Apollo Federation support for building distributed GraphQL architectures with multiple services and schema composition. Federation allows you to split your GraphQL schema across multiple services while presenting a unified API to clients.
3
4
## Capabilities
5
6
### Federation Schema
7
8
Federation-enabled schema class with support for Apollo Federation directives.
9
10
```python { .api }
11
class Schema:
12
"""Apollo Federation schema implementation."""
13
14
def __init__(
15
self,
16
query: Type = None,
17
mutation: Type = None,
18
subscription: Type = None,
19
*,
20
types: List[Type] = None,
21
extensions: List[SchemaExtension] = None,
22
enable_federation_2: bool = False
23
):
24
"""
25
Initialize Apollo Federation schema.
26
27
Args:
28
query: Root query type
29
mutation: Root mutation type (optional)
30
subscription: Root subscription type (optional)
31
types: Additional types to include in schema
32
extensions: Schema extensions
33
enable_federation_2: Enable Apollo Federation 2 features
34
"""
35
```
36
37
**Usage Example:**
38
39
```python
40
import strawberry
41
from strawberry.federation import Schema
42
43
@strawberry.federation.type(keys=["id"])
44
class User:
45
id: strawberry.ID
46
name: str
47
email: str
48
49
@strawberry.type
50
class Query:
51
@strawberry.field
52
def users(self) -> List[User]:
53
return get_all_users()
54
55
# Create federation schema
56
schema = Schema(
57
query=Query,
58
enable_federation_2=True
59
)
60
```
61
62
### Federation Type Decorator
63
64
Creates federated GraphQL object types with entity keys and federation directives.
65
66
```python { .api }
67
def type(
68
cls=None,
69
*,
70
name: str = None,
71
description: str = None,
72
keys: List[str] = None,
73
extend: bool = False,
74
resolvable: bool = True
75
) -> Any:
76
"""
77
Decorator to create federated GraphQL object types.
78
79
Args:
80
cls: The class to convert to a federated type
81
name: Custom name for the GraphQL type
82
description: Description for the GraphQL type
83
keys: List of key fields for entity resolution
84
extend: Whether this extends an existing entity from another service
85
resolvable: Whether this entity can be resolved by this service
86
87
Returns:
88
Federated GraphQL object type
89
"""
90
```
91
92
**Usage Examples:**
93
94
```python
95
# Basic federated entity
96
@strawberry.federation.type(keys=["id"])
97
class User:
98
id: strawberry.ID
99
name: str
100
email: str
101
102
# Multi-key entity
103
@strawberry.federation.type(keys=["id", "email"])
104
class User:
105
id: strawberry.ID
106
name: str
107
email: str
108
109
# Extending entity from another service
110
@strawberry.federation.type(keys=["id"], extend=True)
111
class User:
112
id: strawberry.ID = strawberry.federation.field(external=True)
113
posts: List["Post"]
114
115
@classmethod
116
def resolve_reference(cls, id: strawberry.ID):
117
# This service doesn't store user data, just references
118
return cls(id=id)
119
120
# Non-resolvable entity (just provides additional fields)
121
@strawberry.federation.type(keys=["id"], resolvable=False)
122
class User:
123
id: strawberry.ID = strawberry.federation.field(external=True)
124
computed_field: str
125
```
126
127
### Federation Field Decorator
128
129
Defines federated GraphQL fields with federation-specific directives.
130
131
```python { .api }
132
def field(
133
resolver: Callable = None,
134
*,
135
name: str = None,
136
description: str = None,
137
external: bool = False,
138
requires: str = None,
139
provides: str = None,
140
override_: str = None,
141
used_overridden: bool = False
142
) -> Any:
143
"""
144
Decorator to define federated GraphQL fields.
145
146
Args:
147
resolver: Custom resolver function for the field
148
name: Custom field name
149
description: Field description
150
external: Whether field is defined in another service
151
requires: Fields required from other services to resolve this field
152
provides: Fields this field provides to the entity
153
override_: Service that this field overrides (Federation 2)
154
used_overridden: Whether this field uses overridden field (Federation 2)
155
156
Returns:
157
Configured federated GraphQL field
158
"""
159
```
160
161
**Usage Examples:**
162
163
```python
164
@strawberry.federation.type(keys=["id"])
165
class User:
166
id: strawberry.ID
167
name: str = strawberry.federation.field(external=True)
168
email: str = strawberry.federation.field(external=True)
169
170
@strawberry.federation.field(requires="name email")
171
def display_name(self) -> str:
172
return f"{self.name} <{self.email}>"
173
174
@strawberry.federation.field(provides="title")
175
def latest_post(self) -> "Post":
176
post = get_latest_post(self.id)
177
return Post(id=post.id, title=post.title)
178
179
@strawberry.federation.type(keys=["id"])
180
class Post:
181
id: strawberry.ID
182
title: str = strawberry.federation.field(external=True)
183
content: str
184
185
# Override field from another service (Federation 2)
186
@strawberry.federation.field(override_="posts-service")
187
def view_count(self) -> int:
188
return get_accurate_view_count(self.id)
189
```
190
191
### Federation Interface
192
193
Creates federated GraphQL interfaces.
194
195
```python { .api }
196
def interface(
197
cls=None,
198
*,
199
name: str = None,
200
description: str = None,
201
keys: List[str] = None
202
) -> Any:
203
"""
204
Decorator to create federated GraphQL interfaces.
205
206
Args:
207
cls: The class to convert to a federated interface
208
name: Custom name for the GraphQL interface
209
description: Description for the GraphQL interface
210
keys: Key fields for interface entities
211
212
Returns:
213
Federated GraphQL interface type
214
"""
215
```
216
217
**Usage Example:**
218
219
```python
220
@strawberry.federation.interface(keys=["id"])
221
class Node:
222
id: strawberry.ID
223
224
@strawberry.federation.type(keys=["id"])
225
class User(Node):
226
id: strawberry.ID
227
name: str
228
229
@strawberry.federation.type(keys=["id"])
230
class Post(Node):
231
id: strawberry.ID
232
title: str
233
```
234
235
### Other Federation Decorators
236
237
All core decorators are available with federation support:
238
239
```python { .api }
240
def input(
241
cls=None,
242
*,
243
name: str = None,
244
description: str = None
245
) -> Any:
246
"""Federated GraphQL input type decorator."""
247
248
def enum(
249
cls=None,
250
*,
251
name: str = None,
252
description: str = None
253
) -> Any:
254
"""Federated GraphQL enum type decorator."""
255
256
def scalar(
257
cls=None,
258
*,
259
name: str = None,
260
description: str = None,
261
serialize: Callable = None,
262
parse_value: Callable = None
263
) -> Any:
264
"""Federated GraphQL scalar type decorator."""
265
266
def union(name: str, types: Tuple[Type, ...]) -> Any:
267
"""Federated GraphQL union type creator."""
268
```
269
270
## Entity Resolution
271
272
### Entity Reference Resolution
273
274
Federated entities must implement reference resolution for the Apollo Gateway.
275
276
```python { .api }
277
# Entities automatically get a resolve_reference class method
278
@strawberry.federation.type(keys=["id"])
279
class User:
280
id: strawberry.ID
281
name: str
282
email: str
283
284
@classmethod
285
def resolve_reference(cls, id: strawberry.ID):
286
"""
287
Resolve entity reference from Apollo Gateway.
288
289
Args:
290
id: Entity key value
291
292
Returns:
293
Entity instance
294
"""
295
user_data = get_user_by_id(id)
296
return cls(
297
id=user_data["id"],
298
name=user_data["name"],
299
email=user_data["email"]
300
)
301
```
302
303
### Complex Key Resolution
304
305
For entities with compound keys:
306
307
```python
308
@strawberry.federation.type(keys=["userId", "productId"])
309
class UserProductPreference:
310
user_id: strawberry.ID
311
product_id: strawberry.ID
312
rating: int
313
314
@classmethod
315
def resolve_reference(cls, user_id: strawberry.ID, product_id: strawberry.ID):
316
preference = get_user_product_preference(user_id, product_id)
317
return cls(
318
user_id=preference["user_id"],
319
product_id=preference["product_id"],
320
rating=preference["rating"]
321
)
322
```
323
324
### Multiple Key Variants
325
326
For entities that can be resolved by different key combinations:
327
328
```python
329
@strawberry.federation.type(keys=["id", "email"])
330
class User:
331
id: strawberry.ID
332
email: str
333
name: str
334
335
@classmethod
336
def resolve_reference(cls, **kwargs):
337
if "id" in kwargs:
338
user_data = get_user_by_id(kwargs["id"])
339
elif "email" in kwargs:
340
user_data = get_user_by_email(kwargs["email"])
341
else:
342
raise ValueError("No valid key provided")
343
344
return cls(
345
id=user_data["id"],
346
email=user_data["email"],
347
name=user_data["name"]
348
)
349
```
350
351
## Federation Patterns
352
353
### Service Composition Example
354
355
**Users Service:**
356
```python
357
# users_service.py
358
@strawberry.federation.type(keys=["id"])
359
class User:
360
id: strawberry.ID
361
name: str
362
email: str
363
created_at: datetime
364
365
@classmethod
366
def resolve_reference(cls, id: strawberry.ID):
367
user = get_user_from_database(id)
368
return cls(
369
id=user.id,
370
name=user.name,
371
email=user.email,
372
created_at=user.created_at
373
)
374
375
@strawberry.type
376
class Query:
377
@strawberry.field
378
def user(self, id: strawberry.ID) -> Optional[User]:
379
return User.resolve_reference(id)
380
381
@strawberry.field
382
def users(self) -> List[User]:
383
return [User.resolve_reference(u.id) for u in get_all_users()]
384
385
schema = strawberry.federation.Schema(query=Query)
386
```
387
388
**Posts Service:**
389
```python
390
# posts_service.py
391
@strawberry.federation.type(keys=["id"], extend=True)
392
class User:
393
id: strawberry.ID = strawberry.federation.field(external=True)
394
395
@strawberry.federation.field
396
def posts(self) -> List["Post"]:
397
return get_posts_by_user_id(self.id)
398
399
@strawberry.federation.type(keys=["id"])
400
class Post:
401
id: strawberry.ID
402
title: str
403
content: str
404
user_id: strawberry.ID
405
published_at: datetime
406
407
@strawberry.federation.field
408
def author(self) -> User:
409
return User(id=self.user_id)
410
411
@classmethod
412
def resolve_reference(cls, id: strawberry.ID):
413
post = get_post_from_database(id)
414
return cls(
415
id=post.id,
416
title=post.title,
417
content=post.content,
418
user_id=post.user_id,
419
published_at=post.published_at
420
)
421
422
@strawberry.type
423
class Query:
424
@strawberry.field
425
def post(self, id: strawberry.ID) -> Optional[Post]:
426
return Post.resolve_reference(id)
427
428
schema = strawberry.federation.Schema(query=Query, types=[User])
429
```
430
431
### Advanced Federation Features
432
433
#### Federation 2 Features
434
435
```python
436
# Enable Federation 2 features
437
schema = strawberry.federation.Schema(
438
query=Query,
439
enable_federation_2=True
440
)
441
442
@strawberry.federation.type(keys=["id"])
443
class User:
444
id: strawberry.ID
445
name: str
446
447
# Override field from another service
448
@strawberry.federation.field(override_="legacy-service")
449
def email(self) -> str:
450
return get_updated_email(self.id)
451
452
# Shareable fields (can be defined in multiple services)
453
@strawberry.federation.type(keys=["id"])
454
class Product:
455
id: strawberry.ID
456
457
@strawberry.federation.field(shareable=True)
458
def name(self) -> str:
459
return self._name
460
```
461
462
#### Computed Fields with Requirements
463
464
```python
465
@strawberry.federation.type(keys=["id"], extend=True)
466
class User:
467
id: strawberry.ID = strawberry.federation.field(external=True)
468
first_name: str = strawberry.federation.field(external=True)
469
last_name: str = strawberry.federation.field(external=True)
470
471
@strawberry.federation.field(requires="firstName lastName")
472
def full_name(self) -> str:
473
return f"{self.first_name} {self.last_name}"
474
```
475
476
#### Providing Fields to Other Services
477
478
```python
479
@strawberry.federation.type(keys=["id"])
480
class User:
481
id: strawberry.ID
482
email: str
483
484
@strawberry.federation.field(provides="username")
485
def profile(self) -> "UserProfile":
486
profile = get_user_profile(self.id)
487
return UserProfile(
488
user_id=self.id,
489
username=derive_username_from_email(self.email),
490
bio=profile.bio
491
)
492
493
@strawberry.federation.type(keys=["userId"])
494
class UserProfile:
495
user_id: strawberry.ID
496
username: str = strawberry.federation.field(external=True)
497
bio: str
498
```
499
500
## Federation Schema SDL
501
502
Generate federation-compatible SDL:
503
504
```python
505
from strawberry.printer import print_schema
506
507
# Print federation schema
508
federation_sdl = print_schema(schema)
509
print(federation_sdl)
510
```
511
512
**Example Output:**
513
```graphql
514
extend schema
515
@link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@external", "@requires", "@provides"])
516
517
type User @key(fields: "id") {
518
id: ID!
519
name: String!
520
email: String!
521
posts: [Post!]!
522
}
523
524
type Post @key(fields: "id") {
525
id: ID!
526
title: String!
527
content: String!
528
author: User!
529
}
530
531
type Query {
532
user(id: ID!): User
533
post(id: ID!): Post
534
}
535
```
536
537
## Federation Gateway Integration
538
539
Services register with Apollo Gateway:
540
541
```javascript
542
// gateway.js
543
const { ApolloGateway } = require('@apollo/gateway');
544
const { ApolloServer } = require('apollo-server');
545
546
const gateway = new ApolloGateway({
547
serviceList: [
548
{ name: 'users', url: 'http://localhost:4001/graphql' },
549
{ name: 'posts', url: 'http://localhost:4002/graphql' },
550
{ name: 'reviews', url: 'http://localhost:4003/graphql' }
551
]
552
});
553
554
const server = new ApolloServer({
555
gateway,
556
subscriptions: false
557
});
558
```
559
560
This enables clients to make unified queries across all services:
561
562
```graphql
563
query UnifiedQuery {
564
user(id: "1") {
565
name
566
567
posts {
568
title
569
content
570
reviews {
571
rating
572
comment
573
}
574
}
575
}
576
}
577
```