Skip to content

fix: Composition fails when interface implements another interface#2754

Open
tylerscoville wants to merge 1 commit intowundergraph:mainfrom
tylerscoville:tscoville/covariants
Open

fix: Composition fails when interface implements another interface#2754
tylerscoville wants to merge 1 commit intowundergraph:mainfrom
tylerscoville:tscoville/covariants

Conversation

@tylerscoville
Copy link
Copy Markdown

@tylerscoville tylerscoville commented Apr 9, 2026

We are migrating from Apollo and have a situation where we have an interface that implements an interface that is failing composition with an error like:

│ The Object "ConversationEventEdge" has the following Interface implementation errors:                                  │
│  The implementation of Interface "ConnectionEdge" by "ConversationEventEdge" is invalid because:                       │
│   The field "node" is invalid because:                                                                                 │
│    The implemented response type "ConversationEvent!" is not a valid subtype (equally or more restrictive) of the      │
│ response type "ConnectionNode!" for "ConnectionEdge.node".

I believe an interface implementing another interface is valid, and I took a pass at updating the code to support this. The test schema in subgraphAO, although contrived, mirrors the pattern we have. This is my first time contributing, feel free to let me know if I missed something or if this is not the correct solution/place!

Summary by CodeRabbit

  • New Features

    • Interfaces can now implement other interfaces in federated schemas, enabling more flexible schema designs with covariant return types during composition.
  • Tests

    • Added test coverage for interface-implementing-interface scenarios in schema federation.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/docs-website.
  • I have read the Contributors Guide.

Open Source AI Manifesto

This project follows the principles of the Open Source AI Manifesto. Please ensure your contribution aligns with its principles.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Walkthrough

This pull request enhances type validation logic to consider interface implementation relationships when validating type compatibility. The isTypeValidImplementation function signature expands to accept an optional parent definition data map, enabling validation to recognize when an implementation type is itself an interface that implements the original abstract type. This enhancement is threaded through federation and normalization factories, with corresponding test coverage added.

Changes

Cohort / File(s) Summary
Core Type Validation Enhancement
composition/src/schema-building/utils.ts
Updated isTypeValidImplementation to accept optional parentDefinitionDataByTypeName parameter and expanded validation logic to check if implementationTypeName corresponds to an interface whose implementedInterfaceTypeNames includes the originalTypeName.
Factory Integration
composition/src/v1/federation/federation-factory.ts, composition/src/v1/normalization/normalization-factory.ts
Both factories now pass this.parentDefinitionDataByTypeName to isTypeValidImplementation calls within validation methods (getValidImplementedInterfaces and validateInterfaceImplementations respectively).
Test Coverage
composition/tests/v1/types/interfaces.test.ts
Added federation test case with subgraphAO fixture verifying that an interface implementing another interface is treated as a valid covariant return type, alongside minor whitespace adjustments.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly summarizes the main change: fixing composition logic to support interfaces implementing other interfaces, which is exactly what the PR accomplishes across the modified files.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
composition/src/schema-building/utils.ts (1)

633-633: Minor: Consider line length for readability.

This line is quite long (~130 characters). While functional, breaking it for readability would be a small improvement.

♻️ Optional formatting improvement
-    return isTypeValidImplementation(originalType.type, implementationType.type, concreteTypeNamesByAbstractTypeName, parentDefinitionDataByTypeName);
+    return isTypeValidImplementation(
+      originalType.type,
+      implementationType.type,
+      concreteTypeNamesByAbstractTypeName,
+      parentDefinitionDataByTypeName,
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@composition/src/schema-building/utils.ts` at line 633, Break the long return
statement that calls isTypeValidImplementation for readability: split the
arguments onto separate lines or assign intermediate variables for
originalType.type, implementationType.type, concreteTypeNamesByAbstractTypeName,
and parentDefinitionDataByTypeName before returning; keep the same function call
(isTypeValidImplementation) and same argument order so behavior is unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@composition/src/schema-building/utils.ts`:
- Line 633: Break the long return statement that calls isTypeValidImplementation
for readability: split the arguments onto separate lines or assign intermediate
variables for originalType.type, implementationType.type,
concreteTypeNamesByAbstractTypeName, and parentDefinitionDataByTypeName before
returning; keep the same function call (isTypeValidImplementation) and same
argument order so behavior is unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 63592481-ff0e-4b06-a2bc-f78964393ffa

📥 Commits

Reviewing files that changed from the base of the PR and between 0b0d42d and 673f2bd.

📒 Files selected for processing (4)
  • composition/src/schema-building/utils.ts
  • composition/src/v1/federation/federation-factory.ts
  • composition/src/v1/normalization/normalization-factory.ts
  • composition/tests/v1/types/interfaces.test.ts

@Aenimus
Copy link
Copy Markdown
Member

Aenimus commented Apr 9, 2026

Hi @tylerscoville,

Does the test that you added in this PR fail without your changes? It doesn't look like it resembles the error message you've provided where there appears to be a mismatch in return types for an implementation of an interface.

Perhaps you could add a failing test that resembles the case you're experiencing more closely?

Thanks,

The WunderGraph Team

@tylerscoville
Copy link
Copy Markdown
Author

Hey @Aenimus Thanks for the quick reply! When i revert the code changes and run the tests i see the following error:

composition git:(tscoville/covariants) ✗ pnpm test -- tests/v1/types/interfaces.test.ts -t "covariant"

> @wundergraph/composition@0.55.0 test /Users/tscoville/Code/cosmo/composition
> vitest run "tests/v1/types/interfaces.test.ts" "-t" "covariant"

The CJS build of Vite's Node API is deprecated. See https://vite.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.

 RUN  v3.2.4 /Users/tscoville/Code/cosmo/composition

stdout | tests/v1/types/interfaces.test.ts > Interface tests > Federation tests > that an interface implementing another interface is a valid covariant return type
Error: The subgraph "subgraph-ao" could not be federated for the following reason:
The Object "Pet" has the following Interface implementation errors:
 The implementation of Interface "Edge" by "Pet" is invalid because:
  The field "node" is invalid because:
   The implemented response type "Dog!" is not a valid subtype (equally or more restrictive) of the response type "Node!" for "Edge.node".

    at Module.subgraphValidationError (/Users/tscoville/Code/cosmo/composition/src/errors/errors.ts:403:10)
    at Module.batchNormalize (/Users/tscoville/Code/cosmo/composition/src/v1/normalization/normalization-factory.ts:4215:29)
    at initializeFederationFactory (/Users/tscoville/Code/cosmo/composition/src/v1/federation/federation-factory.ts:3265:18)
    at Module.federateSubgraphs (/Users/tscoville/Code/cosmo/composition/src/v1/federation/federation-factory.ts:3387:35)
    at Module.federateSubgraphs (/Users/tscoville/Code/cosmo/composition/src/federation/federation.ts:21:14)
    at Module.federateSubgraphsSuccess (/Users/tscoville/Code/cosmo/composition/tests/utils/utils.ts:86:18)
    at /Users/tscoville/Code/cosmo/composition/tests/v1/types/interfaces.test.ts:1066:40
    at file:///Users/tscoville/Code/cosmo/node_modules/.pnpm/@vitest+runner@3.2.4/node_modules/@vitest/runner/dist/chunk-hooks.js:155:11
    at file:///Users/tscoville/Code/cosmo/node_modules/.pnpm/@vitest+runner@3.2.4/node_modules/@vitest/runner/dist/chunk-hooks.js:752:26
    at file:///Users/tscoville/Code/cosmo/node_modules/.pnpm/@vitest+runner@3.2.4/node_modules/@vitest/runner/dist/chunk-hooks.js:1897:20

 ❯ tests/v1/types/interfaces.test.ts (50 tests | 1 failed | 49 skipped) 18ms
   ↓ Interface tests > Normalization tests > that an Interface extension orphan is valid
   ↓ Interface tests > Normalization tests > that an Interface can be extended #1
   ↓ Interface tests > Normalization tests > that an Interface can be extended #2
   ↓ Interface tests > Normalization tests > that an Interface stub can be extended #1
   ↓ Interface tests > Normalization tests > that an Interface stub can be extended #2
   ↓ Interface tests > Normalization tests > that an Interface stub can be extended #3
   ↓ Interface tests > Normalization tests > that an Interface stub can be extended #4
   ↓ Interface tests > Normalization tests > that an Interface stub can be extended #5
   ↓ Interface tests > Normalization tests > that an Interface can be extended with just a directive #1
   ↓ Interface tests > Normalization tests > that an Interface can be extended with just a directive #2
   ↓ Interface tests > Normalization tests > that an Interface extension can be extended with just a directive #1
   ↓ Interface tests > Normalization tests > that an Interface extension can be extended with just a directive #2
   ↓ Interface tests > Normalization tests > that an error is returned if a final Interface does not define any Fields
   ↓ Interface tests > Normalization tests > that an error is returned if a final Interface extension does not define any Fields
   ↓ Interface tests > Normalization tests > that an error is returned if a final extended Interface does not define any Fields #1
   ↓ Interface tests > Normalization tests > that an error is returned if a final extended Interface does not define any Fields #2
   ↓ Interface tests > Normalization tests > that an error is returned if an Interface defines a duplicate Field
   ↓ Interface tests > Normalization tests > that an error is returned if an Interface extension defines a duplicate Field
   ↓ Interface tests > Normalization tests > that an error is returned if an extended Interface defines a duplicate Field #1
   ↓ Interface tests > Normalization tests > that an error is returned if an extended Interface defines a duplicate Field #2
   ↓ Interface tests > Normalization tests > that errors are returned if implemented Interface Fields are invalid #1
   ↓ Interface tests > Normalization tests > that errors are returned if implemented interface fields are invalid #2
   ↓ Interface tests > Normalization tests > that an error is returned if a type attempts to implement a type that is not an interface
   ↓ Interface tests > Normalization tests > that an error is returned if an interface attempts to implement itself
   ↓ Interface tests > Normalization tests > that a warning is returned if a Field returns an Interface without any implementations
   ↓ Interface tests > Normalization tests > that an Interface without implementations is valid if it is not used as an output type
   ↓ Interface tests > Federation tests > that an Interface type and extension definition federate successfully #1.1
   ↓ Interface tests > Federation tests > that an Interface type and extension definition federate successfully #1.2
   ↓ Interface tests > Federation tests > that Interfaces merge by union
   ↓ Interface tests > Federation tests > that Interfaces and implementations merge by union
   ↓ Interface tests > Federation tests > that nested Interfaces merge by union
   ↓ Interface tests > Federation tests > that errors are returned if implemented Interface Fields are invalid #2
   ↓ Interface tests > Federation tests > that an error is returned if federation results in an Interface extension orphan
   ↓ Interface tests > Federation tests > that a V1 Interface with @extends directive federates with a base definition #1.1
   ↓ Interface tests > Federation tests > that a V1 Interface with @extends directive federates with a base definition #1.2
   ↓ Interface tests > Federation tests > that an error is returned if federation results in a V1 Interface with @extends directive orphan #1
   ↓ Interface tests > Federation tests > that an error is returned if federation results in a V1 Interface with @extends directive orphan #2.1
   ↓ Interface tests > Federation tests > that an error is returned if federation results in a V1 Interface with @extends directive orphan #2.2
   ↓ Interface tests > Federation tests > that a V2 Interface @extends directive orphan is valid #1
   ↓ Interface tests > Federation tests > that a V2 Interface @extends directive orphan is valid with another base type #1.1
   ↓ Interface tests > Federation tests > that a V2 Interface @extends directive orphan is valid with another base type #1.2
   ↓ Interface tests > Federation tests > that a V2 Interface @extends directive orphan is valid with another extension #1.1
   ↓ Interface tests > Federation tests > that a V2 Interface @extends directive orphan is valid with another extension #1.2
   ↓ Interface tests > Federation tests > that Field named types can coerce implementing types into Interfaces #1.1
   ↓ Interface tests > Federation tests > that Field named types can coerce implementing types into Interfaces #1.2
   ↓ Interface tests > Federation tests > that Field named types can coerce a single implementing type into Interfaces #2.1
   ↓ Interface tests > Federation tests > that Field named types can coerce a single implementing types into Interfaces #2.2
   ↓ Interface tests > Federation tests > that Field named types cannot coerce more than one implementing type into Interfaces #3.1
   ↓ Interface tests > Federation tests > that Field named types cannot coerce more than one implementing type into Interfaces #3.2
   × Interface tests > Federation tests > that an interface implementing another interface is a valid covariant return type 17ms
     → federateSubgraphs failed when expected to succeed: expected false to be true // Object.is equality

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  tests/v1/types/interfaces.test.ts > Interface tests > Federation tests > that an interface implementing another interface is a valid covariant return type
AssertionError: federateSubgraphs failed when expected to succeed: expected false to be true // Object.is equality

- Expected
+ Received

- true
+ false

 ❯ Module.federateSubgraphsSuccess tests/utils/utils.ts:92:79
     90|     }
     91|   }
     92|   expect(result.success, 'federateSubgraphs failed when expected to succeed').toBe(true);
       |                                                                               ^
     93|   return result as FederationSuccess;
     94| }
 ❯ tests/v1/types/interfaces.test.ts:1066:40

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯


 Test Files  1 failed (1)
      Tests  1 failed | 49 skipped (50)
   Start at  15:50:59
   Duration  779ms (transform 280ms, setup 0ms, collect 522ms, tests 18ms, environment 0ms, prepare 78ms)

 ELIFECYCLE  Test failed. See above for more details.
 ```
 
 
 while this is not the exact error message (types are different) it is the same error:
 ```
Error: The subgraph "subgraph-ao" could not be federated for the following reason:
The Object "Pet" has the following Interface implementation errors:
 The implementation of Interface "Edge" by "Pet" is invalid because:
  The field "node" is invalid because:
   The implemented response type "Dog!" is not a valid subtype (equally or more restrictive) of the response type "Node!" for "Edge.node".
 ```



@Aenimus
Copy link
Copy Markdown
Member

Aenimus commented Apr 9, 2026

@tylerscoville Apologies—I've just had a closer look. I see the pertinent part of the test here is Pet.node returning Dog. I'd actually argue the rest of the test schema is a lot of unnecessary noise that distracts from the intention of the test.

Regardless, I glanced at the code, and I do believe there is a problem. I'll take a closer look next week.

@tylerscoville
Copy link
Copy Markdown
Author

@Aenimus Happy to make any changes that clarify things. Let me know if there is anything I can help with.

@Aenimus Aenimus self-assigned this Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants