graphql-federation
install
source · Clone the upstream repo
git clone https://github.com/wundergraph/graphql-federation-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/wundergraph/graphql-federation-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/graphql-federation" ~/.claude/skills/wundergraph-graphql-federation-skill-graphql-federation && rm -rf "$T"
manifest:
skills/graphql-federation/SKILL.mdsource content
GraphQL Federation Skill
Create correct GraphQL federation subgraphs following WunderGraph Cosmo composition rules.
Core Concepts
Subgraph: A GraphQL microservice contributing to the federated graph. Federated Graph/Supergraph: The composed GraphQL interface for clients. Router: Plans operations, routes requests to subgraphs, joins data. Entity: A type with
@key directive resolvable across subgraphs.
Router Schema vs Client Schema: Router sees all fields; clients don't see @inaccessible items.
Essential Directives
@key - Define Entities
# Basic entity type User @key(fields: "id") { id: ID! name: String! } # Multiple keys type Product @key(fields: "id") @key(fields: "sku") { id: ID! sku: String! name: String! } # Composite key type Review @key(fields: "author { id } product { id }") { author: User! product: Product! rating: Int! } # Non-resolvable key (reference only, can't resolve this entity) type User @key(fields: "id", resolvable: false) { id: ID! }
Key Rules:
- Key fields cannot have arguments
- Key fields cannot be abstract types (interfaces/unions)
- Key fields must select leaf scalars or nested objects with selections
- Multiple keys provide alternative resolution paths
@shareable - Allow Multiple Definitions
# When same field exists in multiple subgraphs type User @key(fields: "id") { id: ID! name: String! @shareable # Can be defined in other subgraphs } # Object-level shareable applies to all fields type User @key(fields: "id") @shareable { id: ID! name: String! email: String! }
Shareable Rules:
- Fields defined in multiple subgraphs MUST be @shareable
- Key fields used in other subgraphs' keys must be @shareable
- Root type fields (Query, Mutation) are implicitly shareable
@external - Reference External Fields
# Mark fields resolved by another subgraph extend type User @key(fields: "id") { id: ID! @external name: String! @external reviews: [Review!]! # This subgraph resolves reviews }
External Rules:
- Use on key fields when extending entities
- Use on fields referenced in @requires
- Use on fields referenced in @provides
- External fields are NOT resolved by this subgraph
@requires - Depend on External Data
type Product @key(fields: "id") { id: ID! price: Float! @external currency: String! @external formattedPrice: String! @requires(fields: "price currency") }
Requires Rules:
- Referenced fields must be @external
- Can reference nested fields:
@requires(fields: "details { weight }") - Router fetches required fields before calling this resolver
@provides - Declare Additional Fields
type Query { topProducts: [Product!]! @provides(fields: "name description") } type Product @key(fields: "id") { id: ID! name: String! @external description: String! @external }
Provides Rules:
- Declares that a resolver provides additional fields
- Referenced fields must be @external in this subgraph
- Optimizes router query planning
@override - Transfer Field Ownership
# In new subgraph, taking over from "old-service" type User @key(fields: "id") { id: ID! name: String! @override(from: "old-service") }
Override Rules:
- Cannot override from same subgraph
- Overridden field in source doesn't need @shareable
- Used for gradual migration between subgraphs
@inaccessible - Hide from Client Schema
type User @key(fields: "id") { id: ID! name: String! internalId: String! @inaccessible # Hidden from clients } # Entire type can be inaccessible type InternalMetrics @inaccessible { requestCount: Int! }
Inaccessible Rules:
- Field exists in router schema but not client schema
- Still subject to @shareable rules
- All fields on a type cannot be @inaccessible (type becomes useless)
- Required arguments cannot be @inaccessible alone
@interfaceObject - Treat Interface as Object
# Subgraph A defines the interface interface Media @key(fields: "id") { id: ID! title: String! } type Book implements Media @key(fields: "id") { id: ID! title: String! author: String! } # Subgraph B adds fields to ALL implementations type Media @key(fields: "id") @interfaceObject { id: ID! reviews: [Review!]! # Added to Book and all Media types }
InterfaceObject Rules:
- Cannot define implementations alongside @interfaceObject
- Fields propagate to all concrete implementations
- Must have @key matching the interface's key
Authorization Directives
type Query { publicData: String! privateData: String! @authenticated adminData: String! @requiresScopes(scopes: [["admin"], ["superuser"]]) } # Type-level auth type SecretDocument @authenticated @requiresScopes(scopes: [["read:secrets"]]) { content: String! }
Common Patterns
Entity Extension
# Subgraph A - owns User type User @key(fields: "id") { id: ID! name: String! email: String! } # Subgraph B - extends User with posts extend type User @key(fields: "id") { id: ID! @external posts: [Post!]! } type Post @key(fields: "id") { id: ID! title: String! author: User! }
Federated Mutations
type Mutation { createUser(input: CreateUserInput!): User! } # Entity returned from mutation allows field resolution from other subgraphs type User @key(fields: "id") { id: ID! name: String! }
Interface Entities
interface Node @key(fields: "id") { id: ID! } type User implements Node @key(fields: "id") { id: ID! name: String! } type Product implements Node @key(fields: "id") { id: ID! title: String! }
Error Prevention
Shareability Errors
# ERROR: Field defined in multiple subgraphs without @shareable # Subgraph A type User @key(fields: "id") { id: ID! name: String! # Missing @shareable } # Subgraph B type User @key(fields: "id") { id: ID! name: String! # Conflict! } # FIX: Add @shareable to both type User @key(fields: "id") { id: ID! name: String! @shareable }
Resolvability Errors
# ERROR: Field cannot be resolved from Query entry point # Subgraph A type Query { user: User! # Only in A } type User { id: ID! name: String! # Only in A } # Subgraph B type User { email: String! # Only in B, but User has no @key! } # FIX: Add @key to make User an entity type User @key(fields: "id") { id: ID! name: String! }
External Field Errors
# ERROR: @requires on non-external field type Product @key(fields: "id") { id: ID! price: Float! # Missing @external formattedPrice: String! @requires(fields: "price") } # FIX: Mark as external type Product @key(fields: "id") { id: ID! price: Float! @external formattedPrice: String! @requires(fields: "price") }
Reference Files
For detailed directive specifications, see:
- Complete directive documentationreferences/directives.md
- Composition validation rulesreferences/composition-rules.md
- Advanced federation patternsreferences/patterns.md