Skip to content

Validate schema root operation types - #876

Open
phdoerfler wants to merge 1 commit into
typelevel:mainfrom
phdoerfler:fix/issue-174-root-type-validation
Open

Validate schema root operation types#876
phdoerfler wants to merge 1 commit into
typelevel:mainfrom
phdoerfler:fix/issue-174-root-type-validation

Conversation

@phdoerfler

@phdoerfler phdoerfler commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Fixes #174.

Problem

A schema with no query root type — either because it has no type Query at all and no explicit schema { } block, or because an explicit schema { } block references a root type that doesn't exist — currently either:

  • crashes with an unhelpful NoSuchElementException the first time .queryType is accessed (e.g. on the first query execution), or
  • silently produces a dangling TypeRef for an undefined mutation / subscription root type, since schema { } root-type references were never existence-checked the way ordinary field/type references are.

Per the GraphQL spec, "Root Operation Types":

The query root operation type must be provided and must be an Object type. The mutation root operation type is optional [...] If it is provided, it must be an Object type. Similarly, the subscription root operation type is also optional [...] If it is provided, it must be an Object type.

This should be a schema validation error, not a runtime crash or silent bad state.

Fix

Adds SchemaValidator.validateRootTypes, wired into the existing validateSchema chain right after validateReferences. For each of query (mandatory), mutation (optional), subscription (optional):

  • missing and mandatory → No 'query' root operation type in schema
  • present but undefined → Undefined type '<name>' specified as '<op>' root operation type
  • present, defined, but not an Object type → Type '<name>' specified as '<op>' root operation type is not an object type

Because both the schema"""...""" compile-time macro and the runtime Schema(string) constructor share the same SchemaParser → validateSchema pipeline, this closes the gap at construction time for both entry points. No changes needed to queryType/mutationType/subscriptionType themselves.

Test plan

  • Three new targeted tests in SchemaSuite.scala, one per error path (missing query, dangling reference, non-object root type).
  • ~12 pre-existing test fixtures that relied on Query being optional for schema construction (to unit-test unrelated validators in isolation) updated to include a minimal type Query { foo: Int }.

@phdoerfler
phdoerfler force-pushed the fix/issue-174-root-type-validation branch 2 times, most recently from d0584b5 to 1236982 Compare July 17, 2026 19:22
@phdoerfler
phdoerfler force-pushed the fix/issue-174-root-type-validation branch from 1236982 to 5766d24 Compare August 18, 2026 14:46
@phdoerfler

Copy link
Copy Markdown
Contributor Author

Rebased onto current main. This will fail the build until #899 lands: v0.30.0 is tagged but main's tlBaseVersion is still 0.29, so sbt-typelevel rejects any branch with a commit on top of the tag. Verified locally that with that bump in place this branch is green, MiMa included.

@hugo-vrijswijk
hugo-vrijswijk self-requested a review August 18, 2026 15:48

@hugo-vrijswijk hugo-vrijswijk 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.

Very nice. Thanks! There are some cases where an invalid schema could still be made (type Query { foo: Int } extend schema { query: Undefined } or extend schema { query: Bar }; scalar Bar), but that seems very unlikely to actually occur. So I'm OK merging like this

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.

empty schema should fail at compile time

2 participants