Best Practices for Shared Enums Across Federated Services
Enum drift in distributed GraphQL architectures triggers supergraph composition failures and runtime type resolution errors. When independent subgraphs modify shared enumeration values without coordination, the gateway rejects the schema. This guide provides diagnostic workflows, exact error payloads, and minimal viable configurations for synchronizing enum contracts while preserving service autonomy. For foundational concepts on distributed type ownership, review Subgraph Implementation & Entity Resolution before implementing cross-service enum contracts.
1. Diagnostic Workflow: Identifying Enum Drift
Composition failures typically surface during rover supergraph compose or at gateway startup. Use the following diagnostic path to isolate drift.
Step 1: Capture the Exact Error Payload Federation v2 enforces strict enum alignment. Conflicting definitions generate deterministic payloads:
{
"error": "ENUM_VALUE_MISMATCH",
"message": "Subgraph 'orders-service' and 'fulfillment-service' define enum 'OrderStatus' with conflicting values.",
"details": {
"orders-service": ["PENDING", "PROCESSING", "SHIPPED", "DELIVERED"],
"fulfillment-service": ["PENDING", "PROCESSING", "CANCELLED"],
"missing_in_fulfillment": ["SHIPPED", "DELIVERED"],
"missing_in_orders": ["CANCELLED"]
},
"resolution_hint": "Align enum values across subgraphs or apply @shareable with identical definitions."
}
Step 2: Validate Local Subgraph Schemas Run composition locally before CI:
rover supergraph compose --config supergraph.yaml
If the command exits with exit code 1, parse the output for ENUM_VALUE_MISMATCH or TYPE_MISMATCH.
Step 3: Trace Runtime Resolution Failures
If composition passes but clients receive null or validation errors, inspect gateway resolver logs:
WARN [Gateway] Enum value 'PENDING_V2' received from subgraph 'orders-service' is not defined in the supergraph schema. Dropping field.
2. Minimal Viable Configuration: @shareable Implementation
Federation v2 requires explicit ownership contracts for shared types. Use @shareable to declare enums that multiple subgraphs must define identically.
Subgraph Schema Definition
enum OrderStatus @shareable {
PENDING
PROCESSING
SHIPPED
DELIVERED
CANCELLED
}
Composition Rules
- Every subgraph referencing the enum must declare the exact same value set.
- Omitting
@shareableon a duplicated enum triggersDuplicateTypeDefinitionerrors. - Adding a new value requires simultaneous deployment across all consuming subgraphs, or a phased rollout using deprecation (see Section 4).
Query/Response Validation
query GetOrderStatus {
order(id: "ORD-123") {
id
status
}
}
Expected Response (Aligned):
{
"data": {
"order": {
"id": "ORD-123",
"status": "PROCESSING"
}
}
}
Expected Response (Drifted/Unaligned):
{
"errors": [
{
"message": "Cannot return null for non-nullable field Order.status.",
"path": ["order", "status"]
}
],
"data": null
}
3. CI/CD Contract Testing & Validation Pipeline
Automated drift detection must block deployments before composition. Implement a pre-flight validation step that compares local definitions against a canonical registry.
Validation Script (validate-enums.ts)
import { validateEnumSync } from '@company/schema-registry';
const localEnums = { OrderStatus: ['PENDING', 'PROCESSING', 'SHIPPED'] };
const isValid = await validateEnumSync(localEnums);
if (!isValid) {
throw new Error('Enum drift detected. Sync with registry before deploying.');
}
Pipeline Integration Steps
- Extract: Parse
.graphqlfiles during thebuildstage to extract enum definitions. - Compare: Run the validation script against the registry’s latest published schema.
- Gate: Fail the CI pipeline on mismatch. Require explicit
--force-syncflags for emergency patches. - Publish: On successful merge, update the registry and trigger downstream subgraph schema pulls.
Detailed workflows for synchronizing these types are covered in Managing Shared Enums Across Subgraphs.
4. Safe Deprecation & Migration Path
Removing enum values without coordination breaks downstream clients. Enforce a phased deprecation strategy.
Step 1: Apply @deprecated Directive
enum OrderStatus @shareable {
PENDING
PROCESSING
SHIPPED @deprecated(reason: "Use DELIVERED instead. Removal scheduled for Q3.")
DELIVERED
CANCELLED
}
Step 2: Implement Fallback Resolvers Map deprecated values to their successors in the gateway or subgraph resolver:
const enumFallbackMap: Record<string, string> = {
SHIPPED: 'DELIVERED'
};
export const resolvers = {
Query: {
order: (_, { id }) => {
const order = fetchOrder(id);
return {
...order,
status: enumFallbackMap[order.status] || order.status
};
}
}
};
Step 3: Monitor & Remove Track deprecated enum usage via gateway telemetry. Schedule removal only after:
- Client traffic drops below 0.1% for 14 consecutive days.
- All dependent services have updated their type definitions.
- A maintenance window is scheduled for supergraph composition.
5. Troubleshooting Matrix & Exact Resolutions
| Symptom | Root Cause | Exact Resolution |
|---|---|---|
Composition failed: Duplicate type definition for enum 'OrderStatus' |
@shareable omitted on duplicated enum across subgraphs. |
Add @shareable to the enum declaration in every subgraph. Ensure value sets match exactly. |
Gateway returns null for enum field |
Subgraph returns a value not present in the merged supergraph schema. | Align local enum definitions with the registry. Add missing values to all subgraphs before deployment. |
CI blocks deployment: Enum drift detected |
Local schema diverges from canonical registry. | Run rover subgraph introspect to pull latest registry state. Update local .graphql files and commit. |
Clients receive validation errors after enum removal |
Breaking change deployed without deprecation period. | Revert deployment. Apply @deprecated directive. Maintain fallback resolvers until client migration is verified. |
FAQ & Next Steps
How does GraphQL Federation handle conflicting enum definitions across subgraphs?
Federation v2 merges identical enum definitions marked with @shareable. If conflicting values exist, the composition step fails immediately, requiring teams to align definitions before deployment.
Should enums be managed in a shared package or defined per subgraph?
For strict consistency, use a shared package or schema registry. For service autonomy, define enums locally but enforce synchronization via CI/CD contract testing.
What is the recommended approach for deprecating enum values in a federated graph?
Mark deprecated values with @deprecated(reason: "..."), maintain resolver fallbacks, track gateway usage metrics, and remove only after client migration is verified.
Next Steps
- Audit all subgraphs for unaligned
enumdeclarations. - Implement the
@shareabledirective and registry validation script. - Configure CI gates to block composition on drift.
- Establish a 30-day deprecation SLA for enum value removals.