GraphQL API Versioning: Deprecate Fields and Evolve Your Schema
Learn how to evolve your GraphQL API without breaking changes. Master the expand-and-contract pattern, adding new fields and using @deprecated to sunset data.

Previously in this course, we covered Deploying the Server: GraphQL Production and Cloud Hosting, moving your project from local development to the real world. Now that your API is live, you need a strategy for change. Unlike REST, where you might see /api/v1/ or /api/v2/ in your URL paths, GraphQL encourages continuous evolution on a single endpoint.
In this lesson, we will implement the "expand-and-contract" pattern to evolve your API without forcing breaking changes on your clients.
The Philosophy of API Evolution
In REST, you often solve the problem of breaking changes by versioning the entire API. This is why you see /v1/ and /v2/ in endpoints. This approach is heavy; it forces you to maintain two separate codebases.
GraphQL takes a different approach. Because clients explicitly request only the data they need, you can add new fields to your types without affecting existing queries. The schema is intended to be a living, growing contract. When you need to change a field, you don't remove it—you evolve it.
The Expand-and-Contract Pattern

The standard lifecycle for changing a field involves three steps:
- Expand: Add the new field to your schema while keeping the old one.
- Warn: Use the
@deprecateddirective to signal to developers that the old field is being retired. - Contract: After monitoring your analytics and ensuring no traffic hits the old field, remove it.
Worked Example: Updating a User Model
Let’s say our User type currently has a name field, but we want to split this into firstName and lastName to provide better granularity.
1. Expand
We add the new fields to our existing type definition in our SDL:
GraphQLtype User { id: ID! name: String # We'll deprecate this soon firstName: String lastName: String }
We then update our resolver to populate these new fields. The client can now start migrating their queries to the new structure at their own pace.
2. Warn (Deprecation)
Once the new fields are available, we tell the world the old field is on its way out. GraphQL provides a built-in directive called @deprecated that allows you to specify a reason.
GraphQLtype User { id: ID! name: String @deprecated(reason: "Use firstName and lastName instead") firstName: String lastName: String }
When a developer uses a tool like Apollo Sandbox or a modern IDE, the name field will now appear with a strikethrough, and the reason will show up in the documentation.
Hands-on Exercise: Deprecating a Field
Open your project's typeDefs.js. Find an existing field that you'd like to "refactor" (for example, renaming title to header).
- Add the new field (
header) to your schema. - Apply the
@deprecateddirective to thetitlefield. - Update your resolver to return the same data for both fields temporarily.
- Run your server and inspect the documentation in your GraphQL explorer to see the deprecation message in action.
Common Pitfalls
- Removing fields prematurely: Never delete a field without checking your query logs. Even if you think no one is using it, you might be surprised.
- Ignoring the
reason: When using@deprecated, always provide a clear reason. It serves as your primary communication channel with the developers consuming your API. - Forgetting to update documentation: While the schema is self-documenting, it’s helpful to maintain a changelog alongside your code.
FAQ
Q: Can I change the type of a field?
A: Changing a field's type is a breaking change. Instead, create a new field with a new name (e.g., priceInt vs priceString) and deprecate the old one.
Q: How do I know when it's safe to delete a deprecated field? A: Use server-side logging or analytics tools like Apollo Studio to monitor which fields are still being requested. When usage drops to zero, it's safe to remove.
Q: Does @deprecated actually stop the code from working? A: No, it is purely a metadata flag. It tells developers, "Please stop using this," but the resolver will continue to function until you physically remove it from the code.
Recap

API evolution in GraphQL centers on the idea of non-breaking additive changes. By using the @deprecated directive, you communicate changes clearly, allowing your consumers to migrate their codebases safely. This keeps your API clean, performant, and stable for all users.
Up next: We will explore how to use schema directives to further document and enforce rules within your API.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

Next.js Website & Landing Page Development
A blazing-fast, SEO-optimized website or landing page in Next.js — the kind that loads instantly and ranks. Design-to-code, done right.


