The GraphQL Specification's September 2026 Edition

GraphQL specification editions used to arrive every few years: 2016, 2018, 2021. The September 2025 edition ended a gap of almost four years, and the September 2026 edition follows twelve months later. It is a smaller release than September 2025, which ratified @oneOf and schema coordinates. Its changes are careful ones that matter more than their size suggests, including a new definition of what fragments are for. That one made us look again at a post we published in 2025.
Eight of the eleven members of the GraphQL Technical Steering Committee have approved the September 2026 edition in graphql-spec PR #1244. Under the release process, committee members approve a release on its pull request, and the edition is now waiting to be merged and published. Its draft changelog says it launches together with the first edition of GraphQL over HTTP. Until then, the new text is in the working draft, which is where every quote in this post comes from.
Fragments Get a New Job Description
The September 2025 edition, like October 2021 before it, opened its section on fragments by calling them "the primary unit of composition in GraphQL", and then described them as a way to avoid typing the same fields twice:
Fragments allow for the reuse of common repeated selections of fields, reducing duplicated text in the document.
The new text, from PR #1193 by janettec, Lee Byron and Benjie, keeps the opening sentence and replaces the one about reuse:
Each data-consuming component (function, class, UI element, and so on) of a client application should declare its data needs in a dedicated fragment.
The pull request explains why reuse was the wrong goal, with an example worth retelling. Two functions format a post, one for a feed and one for a notification, and both happen to need the author and the content. Following the old wording, a developer writes one shared fragment for both. Later the feed function needs a timestamp, so the timestamp goes into the shared fragment. The notification function now fetches a field it does not use, and the fragment no longer shows which function needs which field. Removing a field from the shared fragment becomes risky, because it might break the other function.
With one fragment per component, each fragment changes only when its own component changes. Two fragments may contain the same fields today and different fields next month, and that duplication is correct, because the two components have two separate data needs.
We have to admit something here. In 2025 we published a post called "Fragments: GraphQL's Copy-Paste on Steroids", which opens with the DRY principle. To be fair to our past selves, the same post recommends colocating fragments with components and warns against sharing them across unrelated features. The specification has now made that advice the main point, and our headline now disagrees with the specification.
Empty Selection Sets Are Allowed
The grammar for a selection set used to require at least one selection. PR #1227 changes it to zero or more:
SelectionSet : { Selection* }
This sounds like an odd thing to want, until you look at client-only fields. Apollo Client's @client, Relay Resolvers and the @mock directive from GAP-10 all mark fields that the client resolves itself. The client removes those fields from the document before it sends the document to the server. The pull request shows what happens when every field in a selection is client-only:
query LaunchDetails($launchId: ID!) {
launch(id: $launchId) {
isInCart @client
site @client
}
}
After the client removes both fields, launch { } is left, which every edition up to September 2025 rejects as a syntax error. Clients had to work around it, usually by adding __typename. The change also matches what servers could already return: a fragment on a union member that does not match produces an empty object such as {"u":{}} in the response.
Your server needs a GraphQL library that implements the new edition before it accepts this. We sent an empty selection set to graphql-java 26.1, the version in Spring Boot 4.2.0-M2, and it answered with Invalid syntax with offending token '}'. Keep the __typename workaround until your libraries announce support.
Directives Can Be Deprecated
@deprecated has worked on fields, arguments, input fields and enum values. PR #1206 lets directives carry directives of their own, and adds DIRECTIVE_DEFINITION to the locations of @deprecated. A directive can now be marked as deprecated where it is defined, or later with the new extend directive syntax:
directive @cacheHint(maxAge: Int) @deprecated(reason: "Use `@cacheControl`.") on FIELD_DEFINITION
extend directive @legacyAuth @deprecated(reason: "Use `@requiresScope`.")
Introspection follows. __Directive gains isDeprecated and deprecationReason, and __schema.directives takes the same includeDeprecated argument that fields already have. The pull request names Apollo Kotlin as one motivation: its maintainers want to deprecate some of their client-side directives through the schema, the way everything else is deprecated.
graphql-java 26.1 does not know the new introspection fields yet. Our introspection query came back with Unknown field argument 'includeDeprecated', so treat this as a feature to plan for.
OneOf Types Must Be Possible to Fill
The September 2025 edition added OneOf input objects, where the client must set exactly one field. PR #1211 closes a gap in their validation rules. This type was valid under the old rules:
input MovieFilter @oneOf {
filter: MovieFilter
}
Every field of a OneOf type is nullable, so the old check for circular input types accepted it. A client still cannot build a value for it, because the only field it may set requires another MovieFilter, which requires another one, without end. The Circular References section now declares such types invalid. If your schema has one, it was already unusable, and now your tools can tell you so.
Clearer Words for Execution
The rest of the edition is editorial work with real consequences for implementers. PR #894 defines "operation execution" as the Executing Operations part of a request. The specification separates request errors from execution errors by whether an error happens "before execution begins", and that phrase now points at one specific step of the algorithm. PR #1183 and PR #1238 tighten the definition of a response position and clarify which sibling errors appear when an error propagates.
Our error handling course covers the two kinds of error and why the difference matters for clients.
Work Happening Around the Spec
The edition's changelog opens with a section called "GraphQL beyond the specification", signed by Benjie on behalf of the Technical Steering Committee. It lists the work happening next to the core text:
- GraphQL over HTTP, whose first edition launches alongside this one
- GraphQL Auxiliary Proposals (GAPs), a home for optional, shared conventions on top of GraphQL
- The Federation working group, which develops a vendor-neutral way to compose GraphQL schemas, as described in our post on its specification
- The OpenTelemetry working group, which proposed semantic conventions for GraphQL telemetry
- The Golden Path, which explores the default experience libraries and frameworks should offer
- The AI working group, which studies how GraphQL fits new ways of building software
What Did Not Make This Edition
Several larger proposals are still in progress, so they are absent from this release:
@deferand@streamare an RFC 2 draft in PR #1110, which was still under review in October 2026. Our incremental delivery post covers what you can use today.- Error behaviors and the
onErrorrequest property are an RFC 1 proposal in PR #1163. Our onError post explains the three behaviors. - Service capabilities, a way for a server to advertise what it supports, are an RFC 1 proposal in PR #1208.
- Fragment arguments are an RFC 2 draft in PR #1224, and the pull request is labeled as needing a champion.
What to Do About It
Most of this edition does not ask anything new of people who write schemas and queries. For fragments, it asks for a habit. If your client code shares one fragment between unrelated components, split it, and let each component declare what it reads. For the rest, watch the release notes of your GraphQL libraries. Empty selection sets and deprecated directives work only once both your server library and your client tooling support the September 2026 edition.
We tried to end this post with an empty selection set, since the September 2026 edition allows one. Our editor asked for at least one field anyway, so here it is.
Sources
- GraphQL specification, working draft - the text of the September 2026 edition until it is published
- graphql-spec PR #1244 - the September 2026 edition and its changelog
- graphql-spec PR #1193 - the new description of fragments, with the feed and notification example
- graphql-spec PR #1227 - empty selection sets and the client-only field problem
- graphql-spec PR #1206 - directives on directive definitions and deprecated directives
- graphql-spec PR #1211 - OneOf types that cannot hold a finite value
- GraphQL specification, September 2025 edition - the previous edition, for comparison