Repository navigation
Support custom root type names through the schema definition - #2077
andershagbard wants to merge 27 commits into
Conversation
|
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. |
|
It is not that hard. Basically, somewhere in the schema definition - probably schema {
subscription: CustomSubscription
}This definition should be accessible in |
|
Marked this as a draft, as the implementation is not based upon SDL yet.
This is going to become trivial, the test schema can simply include a |
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
🤖 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
…type names 🤖 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
🤖 Generated with Claude Code
…nter 🤖 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
🤖 Generated with Claude Code
# Conflicts: # CHANGELOG.md
|
🤖 @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. |
There was a problem hiding this comment.
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
When a schema definition explicitly sets a root type to the default name (e.g. `schema { mutation:… · Newnew DocumentAST()here creates an instance that bypassesDocumentAST::fromSource()/hydration,… · New Using?: nullconflates a valid search result that is falsy withnull. While GraphQL type names… · New
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
DocumentASTroot 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.
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
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. |
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>



Changes
Root types were always named
Query,MutationandSubscription.Now a schema definition can rename them, as the GraphQL spec allows:
Root fields still resolve through the namespaces configured for queries, mutations and subscriptions.
RootMutation.createUserfinds its resolver inApp\GraphQL\Mutations\CreateUser.Renaming a root type moves no resolver classes, and
lighthouse:queryandlighthouse:mutationkeep generating them where they are found.@validatorclasses 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 Mutationbecomes 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
DefinitionExceptioninstead of being accepted.query, also in federation subgraphs.schema { }, e.g.@link, which would otherwise be dropped. Useextend schemainstead.extend schemathat redefines an operation, or one type used for two operations.Before this change, any
schema { }threwUnknown definition type, so no previously working schema hits these errors.Root type names live on
DocumentAST, not in static state.DocumentAST::$rootTypesmaps 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.TypeValuetakes theDocumentASTas 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
schema { query: RootQuery }Changed
DocumentASTas the second constructor argument ofTypeValueDeprecated
RootType::isRootType(), useDocumentAST::isRootType()