API Versioning through Field Deprecation
Introduction
As your API evolves to support new features or improved functionality, changes to its existing structure might be required. However, directly altering or removing fields in the API responses can lead to breaking changes.These can disrupt the functionality of client applications that rely on those fields, potentially causing errors or failures in application behavior.
Hasura enables you to easily use the built-in @deprecated
schema directive of GraphQL to tag a field as deprecated
with an optional reason. This tag allows you to easily mark fields that are no longer valid or those that have been
updated as deprecated with a note alerting your consumers to the change.
As an example, imagine we have a type Car
with an existing field named engine
which is deprecated in favor of
motor
:
type Car {
id: ID!
make: String!
model: String!
engine: EngineSpec @deprecated(reason: "Use field 'motor' instead")
motor: MotorSpec
}
Consumers of our API will know that engine
has been deprecated, why we've deprecated it, and which field to use in
its place.
You can learn more about this directive in the spec.
Using Field Deprecation in DDN Modeling
The following metadata objects have field deprecation in their GraphQL configuration.
Model
The following example shows how to deprecate a field in a model:
kind: Model
version: V1
definition:
name: Cars
objectType: Car
orderableFields:
- Id
graphql:
selectUniques:
- queryRootField: selectCar
uniqueIdentifier:
- make
- model
deprecated:
reason: Use selectCarById instead
- queryRootField: selectCarById
uniqueIdentifier:
- Id
And the resulting schema:
type Query {
selectCar(make: String!, model: String): Car @deprecated(reason: "use selectCarById instead")
selectCarById(Id: ID!): Car
}
Command
The following example shows how to deprecate a field in a command:
kind: Command
version: V1
definition:
name: GetEngineSpec
outputType: Engine
graphql:
rootFieldName: getEngineSpec
rootFieldKind: Query
deprecated:
reason: "Fuel Engines are no longer supported from Jan 01 2035"
And the resulting schema:
type Query {
getEngineSpec: Engine @deprecated(reason: "Fuel Engines are no longer supported from Jan 01 2035")
}
ObjectType
The following example shows how to deprecate a field in an ObjectType:
kind: ObjectType
version: V1
definition:
name: Car
fields:
- name: Id
type: String
- name: make
type: String
- name: model
type: String
- name: engine
type: String
deprecated:
reason: Use motor field instead
- name: motor
type: String
graphql:
typeName: Car
And the resulting schema:
type Car {
Id: String
make: String
model: String
engine: String @deprecated(reason: "Use motor field instead")
motor: String
}
Relationship
The following example shows how to deprecate a field in a relationship:
kind: Relationship
version: V1
definition:
name: engineSpec
source: Car
target:
command:
name: GetEngineSpec
mapping:
- source:
fieldPath:
- fieldName: engine
target:
argument:
argumentName: name
deprecated:
reason: Engines on cars are no longer supported from Jan 01 2035
And the resulting schema:
type Car {
Id: String
make: String
model: String
engine: String @deprecated(reason: "Use motor field instead")
motor: String
engineSpec: Engine @deprecated(reason: "Engines on cars are no longer supported from Jan 01 2035")
motorSpec: Motor
}
Default Deprecation Reason
In cases where no deprecation reason is explicitly provided, the @deprecated
directive defaults to
No longer supported
as the reason.
The following example from ObjectType metadata illustrates deprecating a field without specifying a reason.
kind: ObjectType
version: V1
definition:
name: Car
fields:
- name: Id
type: String
- name: make
type: String
- name: model
type: String
- name: engine
type: String
deprecated:
reason: null
- name: motor
type: String
graphql:
typeName: Car
And the resulting schema:
type Car {
Id: String
model: String
engine: String @deprecated(reason: "No longer supported")
motor: String
}