You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Only the selected fields from nested types are included (e.g., `description` and `reviewSummary` from `ProductDetails`, not `id` or `title`)
290
288
- Field order in the proto matches the normalized selection order
291
289
290
+
#### Composite Types in Required Fields (Interfaces & Unions)
291
+
292
+
When `@requires` references a field whose type is an interface or union, the required field selection must include inline fragments to specify which fields to extract from each concrete type.
293
+
294
+
##### The `__typename` Requirement
295
+
296
+
When using inline fragments on an interface or union field in `@requires`, `__typename` must be present in the selection set. This is required for the engine to determine which concrete type to deserialize at runtime. Validation produces errors with field paths (e.g., `in "pet.friend"`) for nested selections missing `__typename`.
297
+
298
+
Example:
299
+
300
+
```graphql
301
+
@requires(fields: "primaryItem { __typename ... on PalletItem { name } ... on ContainerItem { name } }")
302
+
```
303
+
304
+
##### Selection Normalization
305
+
306
+
Beforeprotogeneration, selectionsarenormalizedbydistributingparent-levelfieldsintoeachinlinefragment. For example:
307
+
308
+
```graphql
309
+
media { id...onBook { author } ...onMovie { director } }
This ensures each fragment is self-contained. Nested inline fragments on sub-interfaces are also flattened to concrete types. For example, given `Employee` (interface) with implementors `Manager`, `Engineer`, `Contractor`, and `Intern` — where `Engineer` and `Contractor` also implement `Managed`:
319
+
320
+
```graphql
321
+
# Before normalization:
322
+
members {
323
+
id
324
+
...onManaged { supervisor }
325
+
}
326
+
327
+
# After normalization — expanded to all concrete types,
328
+
# with `supervisor` only on types that implement Managed:
329
+
members {
330
+
...onManager { id }
331
+
...onEngineer { idsupervisor }
332
+
...onContractor { idsupervisor }
333
+
...onIntern { id }
334
+
}
335
+
```
336
+
337
+
##### Proto Mapping — Interface/Union Becomes `oneof`
338
+
339
+
The interface or union type maps to a message with `oneof instance` containing each concrete type. `__typename` is consumed during validation only — it does **not** appear in the generated proto.
340
+
341
+
```graphql
342
+
typeStorage@key(fields: "id") {
343
+
id: ID!
344
+
primaryItem: StorageItem!@external
345
+
itemInfo: String!
346
+
@requires(
347
+
fields: "primaryItem { __typename ... on PalletItem { name palletCount } ... on ContainerItem { name containerSize } }"
348
+
)
349
+
}
350
+
351
+
interfaceStorageItem {
352
+
name: String!
353
+
}
354
+
355
+
typePalletItemimplementsStorageItem {
356
+
name: String!
357
+
palletCount: Int!
358
+
}
359
+
360
+
typeContainerItemimplementsStorageItem {
361
+
name: String!
362
+
containerSize: String!
363
+
}
364
+
```
365
+
366
+
Mapsto (Fields message only):
367
+
368
+
```protobuf
369
+
messageRequireStorageItemInfoByIdFields {
370
+
messagePalletItem {
371
+
stringname = 1;
372
+
int32pallet_count = 2;
373
+
}
374
+
375
+
messageContainerItem {
376
+
stringname = 1;
377
+
stringcontainer_size = 2;
378
+
}
379
+
380
+
messageStorageItem {
381
+
oneofinstance {
382
+
ContainerItemcontainer_item = 1;
383
+
PalletItempallet_item = 2;
384
+
}
385
+
}
386
+
387
+
StorageItemprimary_item = 1;
388
+
}
389
+
```
390
+
391
+
**Key Points**:
392
+
393
+
-`__typename` is absent from the proto — it is consumed during validation only
394
+
- The interface type becomes a message with `oneof instance`
395
+
- Each implementing type gets its own message with only the fields selected in its fragment
396
+
- All type messages (`StorageItem`, `PalletItem`, `ContainerItem`) are **nested inside** the `Fields` message. This scoping ensures they don't collide with the root-level messages of the same name, which contain all fields of the type — while the nested versions contain only the subset selected in `@requires`
397
+
- The same pattern applies to union types (union member types instead of implementing types)
398
+
292
399
## Field Resolvers
293
400
294
401
Field resolvers allow you to define custom resolution logic for specific fields within a GraphQL type. Using the `@connect__fieldResolver` directive, you can specify which fields should be resolved through dedicated RPC methods, enabling lazy loading, computed fields, or integration with external data sources.
0 commit comments