GraphQL APIs are expected to evolve continuously instead of shipping /v2.
GraphQL APIs rarely version their endpoint. Instead the schema evolves continuously, relying on one fact: clients receive only the fields they ask for, so adding is safe.
Safe changes: new types, new fields, new optional arguments, new enum values (if clients handle unknown values). Breaking changes: removing or renaming fields, making a nullable field non-null in input positions, making an output field nullable (clients may not expect null), adding a required argument.
Removing things politely:
- Mark it
@deprecated(reason: "Use fullName"). Introspection exposes this, and IDEs strike it through. Since the September 2025 spec edition, deprecation also applies to arguments and input fields. - Measure usage. Field-level usage reporting (Apollo GraphOS, Hive and others) tells you when nobody requests it anymore.
- Remove it once traffic is zero, and use schema checks in CI to catch accidental breaks.
Input design got a new tool in the September 2025 spec: OneOf input objects. input UserBy @oneOf { id: ID email: String } requires exactly one field to be set, replacing ad-hoc validation and enabling input-union style arguments.
Design for the client: name fields after domain concepts rather than database columns, prefer specific fields to generic JSON blobs, and avoid exposing internal IDs or implementation details you might want to change.
Example
type User {
id: ID!
fullName: String!
name: String @deprecated(reason: "Use fullName")
}
"Look a user up by exactly one key"
input UserBy @oneOf {
id: ID
email: String
handle: String
}
type Query {
user(by: UserBy!): User
}
# Valid: user(by: { email: "ann@example.com" })
# Invalid: user(by: { id: "1", email: "ann@example.com" })Remember
- Adding fields is safe because clients select what they need
- Deprecate, measure usage, then remove
@oneOfinputs require exactly one field- Run breaking-change checks in CI
Watch out for
- Changing a field's type in place instead of adding a new field and deprecating the old one.