Using @shareable Directive for Overlapping Types in GraphQL Federation

When multiple microservices independently define identical GraphQL types, the Apollo Router rejects the supergraph during composition due to ambiguous field ownership. This guide provides a deterministic troubleshooting workflow for resolving overlapping type conflicts using the @shareable directive. Proper distribution of type definitions is foundational to scalable GraphQL Federation Architecture & Design. We will cover exact error payloads, diagnostic CLI workflows, minimal viable configurations, and resolver routing implications to ensure zero-downtime schema migrations.

Root Cause: Why Overlapping Types Fail Composition

The Federation composition algorithm enforces strict single-ownership semantics by default. When the router encounters a type defined in multiple subgraphs without explicit merge instructions, it cannot safely union field definitions or determine authoritative resolvers. This triggers an immediate composition failure to prevent ambiguous query routing at runtime.

Exact Router Error Payload:

Error: Type 'User' is defined in multiple subgraphs but is not marked as @shareable.
 -> Subgraph 'auth-service' defines type 'User'
 -> Subgraph 'commerce-service' defines type 'User'
 -> Resolution: Add @shareable to the type definition in both subgraphs, or consolidate ownership.

Legacy Federation v1 relied on @extends to explicitly mark a subgraph as extending an externally owned type. Federation v2 deprecates this pattern in favor of @shareable, which treats overlapping types as a deliberate cross-cutting contract rather than an extension hierarchy. The router fails because it requires an explicit signal to switch from strict ownership validation to field-union merging.

Directive Syntax & Federation v2 Contract Requirements

The @shareable directive must be applied at the type definition level, not on individual fields. It signals to the composition engine that multiple subgraphs are authorized to contribute fields to the same entity. Crucially, every subgraph that defines the overlapping type must include the directive. Omitting it from a single participating subgraph will trigger a merge conflict.

Minimal Viable SDL Configuration:

# Subgraph A (Auth Service)
type User @key(fields: "id") @shareable {
 id: ID!
 email: String!
}

# Subgraph B (Commerce Service)
type User @key(fields: "id") @shareable {
 id: ID!
 orderHistory: [Order!]!
}

The directive instructs the router to perform a structural union of all fields across participating subgraphs. Field signatures (type, nullability, arguments) must remain strictly compatible across all definitions. Divergent signatures will still fail composition regardless of @shareable presence.

Diagnostic & Migration Workflow

Follow this sequential workflow to identify, resolve, and validate overlapping type conflicts in production-bound environments.

  1. Identify Overlapping Types via Rover Run a composition check against your target subgraph to surface exact conflicts:
rover subgraph check my-graph --name commerce-service --schema ./commerce.graphql

Parse the output for Type 'X' is defined in multiple subgraphs errors.

  1. Audit Existing SDLs for Signature Drift Extract the conflicting type from all subgraphs. Verify that field names, return types, and nullability match exactly. Use a schema registry diff tool or rover graph introspect to programmatically compare definitions.

  2. Apply @shareable to All Participating Subgraphs Add @shareable to the type definition in every subgraph that declares it. Do not apply it to subgraphs that only reference the type via @key or field arguments.

  3. Run Local Composition Validation Before pushing to the registry, validate the merged supergraph locally:

rover supergraph compose --config ./federation.yaml > supergraph.graphql

Verify zero errors and inspect the generated SDL to confirm field unioning.

  1. Deploy to Staging & Verify Gateway Routing Push the updated subgraph schema, trigger a router reload, and execute targeted queries against the staging endpoint. Confirm that field resolution routes correctly without fallback or timeout errors.

Before applying the directive, teams must audit existing type boundaries to prevent accidental duplication. Refer to established guidelines on Type Ownership and Shared Schema Contracts to determine which subgraph should act as the canonical owner versus which should declare shared access.

Gateway Routing & Resolver Execution Flow

Marking a type as @shareable does not imply shared resolvers. The gateway maintains strict field-level routing. When a query spans shared types, the router generates a query plan that flattens the request into subgraph-specific fetches.

Gateway Query Execution Plan:

query GetUserWithOrders($id: ID!) {
 user(id: $id) {
 email # Resolved by Auth Service
 orderHistory { # Resolved by Commerce Service
 total
 status
 }
 }
}

The router executes this as:

  1. Fetch 1: Auth Service resolves id and email.
  2. Fetch 2: Router passes the resolved id to Commerce Service to resolve orderHistory.
  3. Merge: Results are stitched at the gateway layer and returned to the client.

Performance Implications:

  • Shared types do not inherently cause N+1 queries, but improper resolver distribution can. If a shared field requires data from another service, use @requires to fetch dependencies efficiently.
  • If two subgraphs accidentally define resolvers for the same shared field, Federation v2 rejects the composition. Use @override(subgraph: "target-service") to explicitly assign resolution authority, or refactor to ensure only one subgraph implements the resolver.

CI/CD Validation & Contract Testing

Automated composition gates are mandatory to prevent @shareable misconfigurations from propagating to production. Implement the following pipeline controls:

  • Pre-Commit Schema Validation: Hook rover subgraph check into your CI runner. Fail the build if composition errors exceed zero.
  • Schema Diffing: Integrate tools like graphql-inspector or rover graph diff to detect signature drift in shared types before merge.
  • Contract Testing: Deploy a lightweight test harness that queries the staging router with representative payloads spanning shared types. Assert response shape, nullability compliance, and latency thresholds.
  • Registry Publishing Gates: Require successful supergraph composition before allowing rover subgraph publish. This ensures the router only accepts validated, merge-ready schemas.

Common Configuration Mistakes & Resolutions

Mistake Root Cause Resolution
Applying @shareable to only one subgraph Composition requires explicit declaration on every subgraph that defines the type. Partial application triggers merge conflicts. Audit all subgraph SDLs using registry queries. Add the directive to each instance of the overlapping type.
Conflicting field types/nullability across subgraphs Federation enforces strict type compatibility. Divergent signatures (e.g., String vs String!) break the composition contract. Align field signatures using shared type definitions, schema registry validation, or pre-deployment diff checks.
Overusing @shareable instead of defining clear ownership Blurred boundaries lead to unresolvable field conflicts, routing ambiguity, and degraded query performance. Establish a single source of truth for core entities. Use @shareable strictly for cross-cutting concerns with well-defined resolver boundaries.

Frequently Asked Questions

When should I use @shareable versus @extends in GraphQL Federation?

Use @extends (or @key alone) when a subgraph is extending a type it does not own and contributes no overlapping fields. Use @shareable when multiple subgraphs independently define and resolve fields on the same type, requiring the router to merge definitions rather than treat one as an extension.

Does marking a type as @shareable impact query performance?

No direct performance penalty occurs at the directive level. However, improper resolver distribution across shared types can cause sequential fetches. Optimize by ensuring fields are resolved by the most efficient subgraph and using @requires/@provides where necessary to batch data fetching.

How do I handle resolver conflicts when two subgraphs define the same shared field?

Federation v2 rejects duplicate field resolvers by default. Use @override to explicitly assign resolution to one subgraph, or refactor to ensure only one subgraph defines the resolver while others only declare the field signature for type completeness.