LabHub

Blog

GraphQL Spec, Almost Four Years Later — What Made It Into the September 2025 Edition and GraphQL.js 17 GA, and What Didn't

한국어English日本語中文

Introduction — A Standard That Seemed Frozen, Moving Again

For a long time, the GraphQL spec's last edition was October 2021. In the meantime the ecosystem kept moving — Apollo Federation and various gateways and clients each evolved on their own — but the standard document itself sat frozen for almost four years. It was a gap wide enough to invite the cynical question, "Is GraphQL in maintenance mode now?"

That gap was broken by two recent releases. In September 2025, the September 2025 edition shipped — in the words of the official announcement, the first edition since October 2021 — and on June 15, 2026, the reference implementation, GraphQL.js v17, went GA. The first v17 alpha landed on npm on May 19, 2022, which means it took more than four years to shed the alpha label.

This post takes the two releases apart without the hype: what actually made it into the standard, what's still missing (spoiler: @defer and @stream), and what your team should do about it right now. Every date and version here was verified directly against the spec text, the npm registry, and GitHub releases.

Get the Dates Right First — Timeline of the Two Releases

GraphQL Spec (spec.graphql.org)
  2015-07  first working draft (July 2015)
  2021-10-26  October 2021 edition
  2025-09-03  September 2025 edition   <- 3 years, 10 months later
  2026-06-04  current working draft updated (prepping the next edition)

GraphQL.js (registry.npmjs.org publish timestamps)
  2022-05-19  17.0.0-alpha.1
  2025-06-11  17.0.0-alpha.9
  2026-05-07  17.0.0-beta.0
  2026-06-02  17.0.0-rc.0
  2026-06-15  17.0.0 GA              <- a little over 4 years after alpha began
  2026-07-03  17.0.2 (current latest)

  16.x is still getting patched: 16.13.0 (2026-02-24), 16.14.2 (2026-06-09)

According to the spec's release notes, the September 2025 edition folds in more than 100 changes from 30 contributors since October 2021, and it also happens to land ten years after the first draft (July 2015).

The September 2025 Edition — What's Worth Taking From Four Years of Changes

The official changelog lists five major changes. Let's take them one at a time.

OneOf Input Objects — Standardizing the "Input Union"

GraphQL has had unions for output types but not for input types, so there was no way to express "look up by ID, or by email, but only one of the two" directly in the schema. Everyone left every field nullable and hand-validated in the resolver. This problem, dragging on for years under RFC #825, has now been standardized as @oneOf.

input UserUniqueCondition @oneOf {
  id: ID
  username: String
  organizationAndEmail: OrganizationAndEmailInput
}

In the spec's own words, exactly one field of a OneOf input object must be provided, and its value must not be null. Fill in two fields and you get a request error; fill in one with null and it's also an error. That validation is now a spec-level input coercion rule, not resolver code. Introspection gains a __Type.isOneOf field so codegen tools can generate the union type correctly.

Schema Coordinates — An Addressing System for Schema Elements

RFC #794 gives every element of a schema a unique string address.

Business                          type
Business.name                     field
SearchCriteria.filter             input field
SearchFilter.OPEN_NOW             enum value
Query.searchBusiness(criteria:)   field argument
@private                          directive
@private(scope:)                  directive argument

The spec guarantees each element has exactly one coordinate. It looks trivial, but it's a shared vocabulary for the tooling ecosystem — schema diffs, lint rules, error reporting, and usage tracking ("who's using this field") had each used their own notation until now. There's already a follow-up RFC proposing that errors carry schema coordinates.

The Rest: Document Descriptions, Expanded Deprecation, Full Unicode

Taken as a whole, this edition isn't a feast of new features — it's four years of consistency debt paid down, plus two substantial language features (OneOf, Coordinates). The official announcement frames it around being AI-ready, but the substance is closer to solid standards maintenance.

What Didn't Make the Spec — defer and stream Are Still an RFC

This is the part of the release that deserves the most honest look. @defer and @stream — incremental delivery, splitting a response and sending it out in pieces — are not in the September 2025 edition. I confirmed this by searching the spec text directly: the word "defer" appears zero times. It's not in the June 2026 working draft either.

To get a sense of how old this story is: the graphql package's npm registry has carried an experimental-stream-defer experimental build since October 2020, and that dist-tag still points to a 16.1.0-series build published in December 2021. The Incremental Delivery RFC currently sits at RFC 2 (Draft) — it's not dead. A commit landed on the spec draft the very week I'm writing this (2026-07-15). But that doesn't change the fact that it's been "almost done" for more than five years.

Why does it take this long? Part of it is the difficulty of standardizing execution semantics (duplicate field handling, error propagation) and the payload format, but part of it is also that real-world data isn't uniform. At GraphQLConf 2025, Meta announced its own directive called @async, citing the hidden costs @defer leaves behind — which means the organization running GraphQL at the largest scale in the world is experimenting with an alternative to defer. The very target of standardization is still shifting under everyone's feet.

The practical implication is clear. Any stack using defer/stream today is running a pre-spec feature and carries the risk that the payload format can change. It already has — that's the v17 story right below.

GraphQL.js 17 — What Breaks, What Gets Better

The official upgrade guide is unusually thorough, so here I'll just pull out the essentials you need to make a call.

What Breaks

Executor Separation — execute Is Single-Result Only

The most conspicuous design decision in v17 is the physical separation of stable and experimental features. execute() is now a stable, single-result-only executor, and it outright rejects a schema that has @defer/@stream directives. To use incremental delivery you have to call a separate function whose name nails down that it's experimental.

import { experimentalExecuteIncrementally } from 'graphql';

const result = await experimentalExecuteIncrementally({ schema, document });

if ('initialResult' in result) {
  send(result.initialResult);
  for await (const subsequent of result.subsequentResults) {
    send(subsequent);
  }
} else {
  send(result); // for cases that didn't need incremental delivery
}

A change that matters to teams who put defer/stream into production during the alpha years: the payload format changed. The early alpha format identified payloads by path and label, and field data could be duplicated; the current format registers pending entries by id and announces completion with completed. legacyExecuteIncrementally() remains for hosts that need the old format, but as the name says, it's for a transition period. This is a concrete example of the bill that comes due for using a pre-spec feature early.

There's also one experimental feature on the error-propagation side. Attaching @experimental_disableErrorPropagation to an operation makes an execution error on a non-null field null out just that location instead of cascading null up through its ancestors — aimed at the problem where, in clients using normalized caches, a nulled-out ancestor gets mistaken for a real data update. It's a preview implementation of the ongoing error-behavior (onError) spec discussion, and again, "experimental" is baked right into the name.

What's Useful for Operators — AbortSignal, diagnostics_channel, Harness

Beyond what breaks, what you gain is substantial too.

In other words, v17's real substance isn't defer/stream — it's the server operations surface: cancellation, instrumentation, extension points.

Where Federation Standardization Stands — Composite Schemas Is at Stage 0

A status check for anyone waiting on "standardization of federated GraphQL." The Apollo Federation covered in an earlier post is, after all, just one vendor's spec, and since May 2024 the GraphQL Foundation has been building a vendor-neutral standard in the Composite Schemas Working Group.

The honest state of things as of July 2026: the spec repository's README still states Stage 0 (Preliminary) — pre-Draft, and content is subject to change. GitHub releases: zero. That said, it's nowhere near a dead project — several commits refining the @key/@require rules merged as recently as July 9, 2026, and in a May 2026 GraphQLConf keynote, The Guild's Uri Goldshtein assessed that federation is no longer a frontier experiment but a well-paved road the industry now walks with confidence. The practical takeaway: if you're designing a federated architecture right now, you're still picking a vendor spec (the Apollo Federation v2 family), and Composite Schemas is something to track only as "a direction you might switch to someday."

One governance-side change is also worth recording. GAPs (GraphQL Auxiliary Proposals), announced in June 2026, is an official repository that gathers conventions the core spec deliberately doesn't cover — things like pagination connections and @cost — into community specs. Core spec kept narrow, surrounding conventions routed to GAPs, distributed execution routed to Composite Schemas — the picture is standardization splitting into three layers. For what it's worth, around the same time, Meta used its GraphQLConf 2026 keynote ("The Creator's Curse") to reflect that the ten-year-old promise of being "easy to learn and use" hadn't fully held up at a scale of thousands of engineers, and talked about an internal redesign, while Apollo CEO Matt DeBergalis declared that "GraphQL can and must be the language of AI." The standard is in maintenance mode while vendors tell an AI story — there's a temperature gap at this moment.

So What Should You Do Now

Here's a summary as decision criteria.

Clear reasons to move to v17

Cases where there's no need to rush

Be more conservative about defer/stream

One more thing — from a schema-design standpoint, @oneOf is something you can get for free right away. If you have input types with a "look up by one of several keys" pattern, the refactor of moving resolver validation code into a schema declaration is low-risk, and client type-generation quality improves immediately. Of course, this too requires first confirming that your server and client libraries have implemented the September 2025 edition — there's always a lag between a spec release and ecosystem implementation.

Closing

To sum up: the GraphQL standard broke almost four years of silence and returned with the September 2025 edition, and its contents lean practical — consistency debt paid down plus OneOf plus Schema Coordinates — rather than a showcase of flashy new features. The reference implementation, GraphQL.js 17, came out of four years of alpha as a GA release that drew the boundary between stable execution and experimental features (defer/stream, error-propagation control) at the code level. Meanwhile defer/stream is on its tenth year outside the standard, and the federation standard (Composite Schemas) sits at Stage 0.

You could read this picture cynically — "four years for this?" But reading it the other way round is more useful in practice. This is a project that chooses to wait ten years rather than put a breaking change into the spec, which is exactly why a server built against the October 2021 spec is still valid today. Stability is the default, and experiments come labeled as such — that's a property worth wanting from the foundational technology of your API layer. Compared against how Protobuf tightens its defaults through an Edition system around the same time, you can see both camps converging on "managing migration cost over chasing innovation." Instead of spending time disappointed at the standard's pace, go draft your @oneOf refactor and your v17 upgrade estimate.

References

Comments

No comments yet.

Sign in to leave a comment