Skip to content

Experimental Subschema empty interface#5184

Open
tomgasson wants to merge 2 commits intofacebook:mainfrom
tomgasson:sub-schema-empty-interface
Open

Experimental Subschema empty interface#5184
tomgasson wants to merge 2 commits intofacebook:mainfrom
tomgasson:sub-schema-empty-interface

Conversation

@tomgasson
Copy link
Contributor

In order to work out what part of our very large schema is actually being used by our products, we've been exploring using the subschema extraction. Our intent is to identify dead / unused schema and make our breaking changes detection & test selection more fine-grained (specific to the consuming product)

We've run into a problem of producing invalid schema.

schema

type Query {
  search: Searchable
}

interface Searchable {
  title: String
  score: Float
}

type Article implements Searchable {
  title: String
  score: Float
  body: String
}

usage

graphql`
  query SearchQuery {
    search {
      ... on Article {
        body
      }
    }
  }
`;

output

type Article implements Searchable {
  body: String
}

type Query {
  search: Searchable
}

interface Searchable

which is invalid, since Searchable has no fields.

  1. We can't remove Searchable because it appears as an output type of Query.search: Searchable and changing that would change the schema
  2. We can't "fake" it with a dummy field because the Article would then need to implement that dummy field too
  3. We can't use __typename (which is effectively what happens at runtime) because __ is reserved

This PR implements a "fix": If we find empty interfaces, we force their fields existence (and hence their implementing concrete types to have those fields). This means we do have "unused fields", but only for these cases.

Not ideal, however this only occurs 12 times for us so is acceptable, and I expect it's not encountered in Meta's schema if it's not yet addressed.

Interfaces that are only reached via inline fragments on implementing
types end up with missing or zero fields in the generated sub-schema.

The snapshots capture the current (broken) behavior:
- interface_empty_without_id: `interface Searchable` has zero fields
- interface_only_inline_fragments: `interface Node` missing `createdAt`
- interface_on_object_field: `interface FeedItem` missing `title`
- interface_nested: `interface Event` missing `name`
Add a `backfill_empty_interfaces` option to UsedSchemaCollectionOptions.
When enabled, after the IR walk completes, any interface that ended up
with zero fields gets all its fields touched from the full schema.

This handles the case where an interface is referenced as a return type
(so it must exist in the sub-schema) but is only accessed via inline
fragments on implementing types (so no fields were directly selected
on it). Without this, the output contains `interface Foo` with no
fields — invalid per the GraphQL spec.

Interfaces that already have at least one field are left alone, so
this only kicks in for the truly empty case.

Enabled for experimental-regenerate-sub-schema.
@meta-cla meta-cla bot added the CLA Signed label Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant