Skip to content

Support custom root type names through the schema definition - #2077

Open
andershagbard wants to merge 27 commits into
nuwave:masterfrom
andershagbard:custom-roottypes
Open

andershagbard wants to merge 27 commits into
nuwave:masterfrom
andershagbard:custom-roottypes

Conversation

@andershagbard

@andershagbard andershagbard commented Mar 3, 2022 •

Copy link
Copy Markdown
Contributor
  • Added or updated tests
  • Documented user facing changes
  • Updated CHANGELOG.md

Changes

Root types were always named Query, Mutation and Subscription.
Now a schema definition can rename them, as the GraphQL spec allows:

schema {
  query: RootQuery
  mutation: RootMutation
}
Root fields still resolve through the namespaces configured for queries, mutations and subscriptions.

RootMutation.createUser finds its resolver in App\GraphQL\Mutations\CreateUser.
Renaming a root type moves no resolver classes, and lighthouse:query and lighthouse:mutation keep generating them where they are found.
@validator classes still follow the type name, e.g. Validators\RootMutation\CreateUserValidator, so users who rename a root type move those.

With a schema definition, only the types it lists are root types.

An unlisted type Mutation becomes a regular object type.
Without a schema definition, the defaults apply as before.
Operation types in extend schema { ... } now set root types too, even without a schema definition.
Before, Lighthouse ignored them.

Invalid schema definitions throw a DefinitionException instead of being accepted.
  • A schema definition without query, also in federation subgraphs.
  • Directives on schema { }, e.g. @link, which would otherwise be dropped. Use extend schema instead.
  • A custom root type name without a matching type definition.
  • A second schema definition, an extend schema that redefines an operation, or one type used for two operations.

Before this change, any schema { } threw Unknown definition type, so no previously working schema hits these errors.

Root type names live on DocumentAST, not in static state.

DocumentAST::$rootTypes maps the default names to the actual ones and is part of the schema cache.
Caches written by older versions load with the default names.
Checks at schema build time ask DocumentAST::isRootType().
Checks at request time, in CacheControl and @defer, compare against the root types of the built schema.
TypeValue takes the DocumentAST as an optional second constructor argument.
The federation printer now prints the schema definition when root names differ from the defaults.

Breaking changes

None.
RootType::isRootType() is deprecated, because it only knows the default names.
Use DocumentAST::isRootType().

This takes over the original config-based implementation by @andershagbard and bases it on SDL, as discussed above.

🤖 Generated with Claude Code

Changelog

Added

  • Support custom root type names through the schema definition schema { query: RootQuery }

Changed

  • Require DocumentAST as the second constructor argument of TypeValue

Deprecated

  • Deprecate RootType::isRootType(), use DocumentAST::isRootType()

@spawnia

spawnia commented Mar 3, 2022

Copy link
Copy Markdown
Collaborator

We should use native SDL for changing the name of the root types, see http://spec.graphql.org/draft/#sec-Schema

@spawnia spawnia added the enhancement A feature or improvement label Mar 3, 2022
@andershagbard

Copy link
Copy Markdown
Contributor Author

We should use native SDL for changing the name of the root types, see http://spec.graphql.org/draft/#sec-Schema

Might be a bit out of my knowledge for Lighthouse and GraphQL. Could use some help if anyone is up for it.

@spawnia

spawnia commented Mar 3, 2022

Copy link
Copy Markdown
Collaborator

It is not that hard. Basically, somewhere in the schema definition - probably schema.graphql - there can be a definition like this:

schema {
  subscription: CustomSubscription
}

This definition should be accessible in DocumentAST.

Comment thread src/Schema/RootType.php Resolved
@spawnia
spawnia marked this pull request as draft March 4, 2022 08:47
@spawnia

spawnia commented Mar 4, 2022

Copy link
Copy Markdown
Collaborator

Marked this as a draft, as the implementation is not based upon SDL yet.

I am not sure how it should be tested whether actually changing the values work?

This is going to become trivial, the test schema can simply include a schema { ... } definition with renamed root query types then.

@andershagbard andershagbard changed the title POC - Add changeable root types WIP - Add changeable root types Mar 4, 2022
@spawnia spawnia changed the title WIP - Add changeable root types Add changeable root types Aug 14, 2022
@spawnia spawnia changed the title Add changeable root types Allow changing root type names Aug 14, 2022
Resets the tree to master: the config-based implementation is replaced
by one based on the SDL schema definition.
Root types are read from `schema { query: ... mutation: ... subscription: ... }`
and `extend schema { ... }` instead of being hardcoded to Query, Mutation and
Subscription. Without a schema definition, the default names still apply.

Co-authored-by: Anders Søgaard <andershagbard@gmail.com>

🤖 Generated with Claude Code
🤖 Generated with Claude Code
🤖 Generated with Claude Code
🤖 Generated with Claude Code
They were silently ignored, e.g. a federation @link would not be detected.

🤖 Generated with Claude Code
It only knows the default root type names.

🤖 Generated with Claude Code
🤖 Generated with Claude Code
Throw on a second schema definition, an extension redefining a root
operation type, and one type used for multiple root operation types.
Also align exception wording with the spec.

🤖 Generated with Claude Code
Pass strict as a named argument, split nested calls, hoist the root
types out of the cache control visitor and drop a redundant docblock.

🤖 Generated with Claude Code
🤖 Generated with Claude Code
@spawnia
spawnia requested a balanced review from Copilot October 11, 2026 11:51
@spawnia spawnia changed the title Allow changing root type names Support custom root type names through the schema definition Oct 11, 2026
@spawnia

spawnia commented Oct 11, 2026

Copy link
Copy Markdown
Collaborator

🤖 @andershagbard I finished this PR on your branch, based on the SDL schema definition instead of config. Do you want to review it before we merge?

The description above summarizes the changes. Your original commits are still in the branch history.

@spawnia
spawnia marked this pull request as ready for review October 11, 2026 11:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

3 open findings
What changed in this PR

Enables custom root operation type names (Query/Mutation/Subscription) via the GraphQL schema { ... } definition, and updates schema building, resolver discovery, and related directives to respect those custom names.

Changes:

  • Parse and persist root operation type mappings in DocumentAST, including validations around schema definitions/extensions.
  • Update schema building and multiple runtime checks (auth, cache control, federation, subscriptions, global ID) to use DocumentAST root type mappings.
  • Add unit/integration tests plus documentation and changelog entries covering custom root type behavior.
File Description
tests/​Unit/​Schema/​TypeRegistryTest.php Adds coverage for handling object types when DocumentAST is not set on the registry.
tests/​Unit/​Schema/​AST/​DocumentASTTest.php Adds unit tests for root type mapping defaults/overrides, serialization, and validation errors.
tests/​Unit/​Auth/​CanDirectiveTest.php Ensures mutation-root checks also work with custom mutation root type names.
tests/​Integration/​Subscriptions/​SubscriptionTest.php Verifies subscription channel generation works with custom subscription root type names.
tests/​Integration/​RootTypesTest.php Adds end-to-end coverage for custom roots, omitted roots, resolver suggestions, undefined roots, and node field injection.
tests/​Integration/​Federation/​FederationSchemaTest.php Confirms _service { sdl } prints a schema definition when custom root names are used.
tests/​Integration/​CacheControl/​CacheControlDirectiveTest.php Verifies cache-control behavior for a custom query root type.
src/​Schema/​Values/​TypeValue.php Threads DocumentAST into TypeValue and uses it for query-root cache key exemptions.
src/​Schema/​Values/​FieldValue.php Resolves namespaces for root fields using DocumentAST root type mapping.
src/​Schema/​TypeRegistry.php Passes DocumentAST into TypeValue during type handling/field loading.
src/​Schema/​SchemaBuilder.php Builds schema using mapped root type names and adds optionalRootType() helper logic.
src/​Schema/​RootType.php Deprecates RootType::isRootType() in favor of DocumentAST::isRootType().
src/​Schema/​ResolverProvider.php Uses DocumentAST to detect root types and to propose resolver commands based on default root op type.
src/​Schema/​Factories/​FieldFactory.php Detects subscription root type using DocumentAST mapping.
src/​Schema/​Directives/​WithDirective.php Disallows usage on root types using DocumentAST::isRootType().
src/​Schema/​Directives/​WithCountDirective.php Disallows usage on root types using DocumentAST::isRootType().
src/​Schema/​AST/​DocumentAST.php Parses schema definition/extension root operation types, stores rootTypes, and adds helpers and validations.
src/​Schema/​AST/​ASTBuilder.php Allows extending root types with custom names by consulting DocumentAST::isRootType().
src/​GlobalId/​NodeDirective.php Injects node field into the mapped query root type.
src/​Federation/​SchemaPrinter.php Prints schema definition via parent printer (supports custom root type names) and appends schema extension directives.
src/​Federation/​FederationPrinter.php Uses schema accessors for root types instead of assuming default root names.
src/​Federation/​ASTManipulator.php Ensures federation root fields are added to the mapped query root type.
src/​Defer/​DeferrableDirective.php Detects mutation root fields via schema mutation type instance rather than name.
src/​CacheControl/​CacheControlServiceProvider.php Detects root fields via schema root type instances rather than default name checks.
src/​Auth/​CanResolvedDirective.php Applies mutation root restrictions using the mapped mutation root type name.
src/​Auth/​CanDirective.php Applies mutation root restrictions using the mapped mutation root type name.
src/​Async/​AsyncDirective.php Restricts @async to mutation root using the mapped mutation root type name.
docs/​master/​the-basics/​schema.md Documents how to declare custom root type names via schema definition.
docs/​6/​the-basics/​schema.md Same documentation update for v6 docs.
CHANGELOG.md Adds release notes for custom root type names and deprecates RootType::isRootType().

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Schema/SchemaBuilder.php Resolved
Comment thread src/Schema/Values/TypeValue.php Resolved
Comment thread src/Schema/AST/DocumentAST.php Resolved
A fallback `new DocumentAST()` left `$hash` uninitialized.
`TypeValue` is not marked `@api`, so the signature may change in a minor release.

🤖 Generated with Claude Code
@andershagbard

Copy link
Copy Markdown
Contributor Author

🤖 @andershagbard I finished this PR on your branch, based on the SDL schema definition instead of config. Do you want to review it before we merge?

The description above summarizes the changes. Your original commits are still in the branch history.

I don't have deep enough understanding of the repo, or time to dive into it, to give a truly good review of the changes.

I think the tests look good. I don't have anything against merging as is.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Cross-cutting root-type handling needs human validation, and federation still accepts undefined custom query roots contrary to the stated requirements.

1 open issue
3 resolved since last review

Review effort: Balanced

Comment thread src/Federation/ASTManipulator.php Resolved
Ensure query type is defined and handle errors for non-root query types.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement A feature or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants