Skip to content

[6.x] Improve type handling in GraphQL schemas#14677

Open
lostgeek wants to merge 6 commits into
statamic:6.xfrom
lostgeek:feat/graphql-type-patch
Open

[6.x] Improve type handling in GraphQL schemas#14677
lostgeek wants to merge 6 commits into
statamic:6.xfrom
lostgeek:feat/graphql-type-patch

Conversation

@lostgeek
Copy link
Copy Markdown
Contributor

Improved GraphQL Types for Entries, Terms, and Assets

This PR tries to tackle the same issue that #14618 tried. So similarly, if you want to approach this topic in a different way, feel free to close this until v7. In any case, we will likely be patching these changes into our Statamic projects, so feel free to approach me for more feedback in the future, if you close this.

Closes #12974.

Summary

This PR introduces concrete type resolution and dynamic union types for the Entries, Terms, and Assets GraphQL fieldtypes. Instead of always returning the generic interface type (e.g. EntryInterface, TermInterface, AssetInterface), fieldtypes now resolve to the most specific type possible based on their blueprint configuration, and dynamic union types are generated if multiple blueprints are possible (e.g. DynamicEntryUnionType).

It also adds per-collection and per-taxonomy top-level queries that return typed results, so consumers can query e.g. blogPosts { ... } of type [Entry_BlogPosts_Article] instead of entries(collection: ["blog_posts"]) { ... } of type [EntryInterface].

Both features are controlled through a new improved_types config section in config/graphql.php.

What changed

Configstatamic.graphql.improved_types is now a structured array:

  • enabled (default true, env STATAMIC_GRAPHQL_IMPROVED_TYPES) — controls fieldtype improved type resolution.
  • collections — list of collection handles (or '*' for all) that get dedicated typed queries.
  • terms — list of taxonomy handles (or '*' for all) that get dedicated typed queries.

Entries fieldtype — When improved_types.enabled is true:

  • No configured collections in blueprint → returns EntryInterface (unchanged).
  • Single collection with one blueprint → returns the concrete EntryType (e.g. Entry_BlogPosts_Article).
  • Multiple blueprints across one or more collections → returns a dynamically generated DynamicEntryUnionType scoped to exactly those blueprint types.
  • Lists use [Type!] (non-null items) instead of [Type].

Terms fieldtype — Same logic as Entries, using TermType / DynamicTermUnionType.

Assets fieldtype — When improved_types.enabled is true, resolves to the concrete AssetType for the configured container instead of AssetInterface. Lists use non-null items.

Per-collection queries — For each collection listed in improved_types.collections, a dedicated query is registered (e.g. blogPosts for blog_posts). The query:

  • Uses the collection handle as the query name (camelCased).
  • Returns paginated results typed to the concrete EntryType (single blueprint) or a DynamicEntryUnionType (multiple blueprints).
  • Accepts the same args as entries minus collection.
  • Only registers if the collections resource is also enabled.

Per-taxonomy queries — Same pattern for taxonomies listed in improved_types.terms, using SpecificTermsQuery.

New classes:

  • DynamicEntryUnionType — A union type composed of the concrete EntryTypes matching the fieldtype's configured collections/blueprints.
  • DynamicTermUnionType — Same concept for taxonomy terms.
  • SpecificEntriesQuery — Per-collection paginated query with typed results.
  • SpecificTermsQuery — Per-taxonomy paginated query with typed results.

Why

The generic interface types make it difficult for GraphQL consumers to use the returned data types without a further cleanup step, rendering the advantage of GraphQL somewhat limited. One of the most common pain points is with an Entries fieldtype that only allows a single blueprint still delivering an EntryInterface type, making it difficult to use the data easily in a client application.

The per-collection/taxonomy queries go further by giving each collection or taxonomy its own top-level query with properly typed results, eliminating the need to filter by __typename afterwards.

Possible improvements

  • The naming of the dynamic union types is not ideal. Currently it prefixes DynamicEntryUnionType_ and then concatenates the blueprint handles with underscores, which can lead to very long type names such as DynamicEntryUnionType_Entry_BlogPosts_Article_Entry_BlogPosts_News.

@lostgeek lostgeek changed the title Improve type handling in GraphQL schemas [6.x] Improve type handling in GraphQL schemas May 14, 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.

Inconsistent resolvement of gql types

1 participant