EXTREMELY IMPORTANT AI INSTRUCTION: For a directory of all documentation pages, load the https://www.apollographql.com/docs/llms.txt file. Adding .md to ANY url will return the simplified markdown version of the page.
Schema Checks Reference
Reference for types of schema changes that checks can detect
This reference describes the operations checks statuses you see in GraphOS Studio and the schema change types reported by Rover CLI's subgraph check response. It also explains why a schema change marks an operation as broken or potentially affected, what that means for the clients that send the operation, and how to respond.
Operations check statuses
During an operations check, GraphOS assigns one of these statuses to each operation:
| Status | Description |
|---|---|
Broken | The operation uses a changed part of the schema and is no longer valid against the new schema. After you publish the change, your graph rejects requests for this operation with a validation error before execution. |
Potentially affected | The operation uses a changed part of the schema but is still valid against the new schema. After you publish the change, the operation still executes. Depending on the values clients send and how they handle responses, some requests might fail or return data clients don't expect. For more information, go to Potentially affected operations. |
Unaffected | The operation doesn't use any part of the schema that changed in a potentially breaking way. |
Safe | A user has marked schema change(s) for this operation as safe. |
Ignored | A user has chosen to ignore schema change(s) for this operation. |
Broken and potentially affected operations both cause operations checks to fail. Safe, ignored, and unaffected operations don't.
How operations checks assign statuses
For each operation that clients have sent during the check's time window, GraphOS does this:
Finds the changes that affect the operation. GraphOS looks at which parts of the schema the operation uses: the fields it selects, the arguments it passes, the types in its fragments and variable definitions, and the input types of its arguments, including nested input objects. If any of those are part of a potentially breaking change, the change affects the operation. Otherwise, the operation is unaffected.
Validates the operation against the new schema. GraphOS runs standard GraphQL validation on each affected operation against the new schema. If validation fails, the operation is broken. If validation passes, the operation is potentially affected.
The status applies to the whole operation. If multiple changes affect an operation and any one of them makes the operation invalid, the operation is broken.
Potentially affected operations
GraphQL validation only checks the operation against the schema. It doesn't know what clients send in variables, what data clients receive, or how client code uses that data. When a change could affect clients in one of those ways without making the operation text invalid, GraphOS marks the operation as potentially affected instead of broken.
Common reasons an operation is potentially affected:
The operation passes values through variables. The operation text names only the variable's type, such as
$input: SaveQuestionInput!, not the values inside it. If you remove an input object field or an enum value, the check can't tell whether your clients send that field or value. Requests that send it fail at runtime.The change affects the response. Changes such as removing an enum value or changing a field from
String!toStringdon't make an operation invalid, but they change the data your clients can receive. Client code that expects the old values or assumes a field is nevernullmight stop working.The operation relies on a default value. If the operation omits an argument or input object field that has a default value, changing that default can change the operation's results.
These examples demonstrate how the same kinds of changes can result in different statuses:
Example of an input object field removal
Suppose you remove minEntries from this input type:
type Mutation {
saveQuestion(input: SaveQuestionInput!): Question
}
input SaveQuestionInput {
text: String!
minEntries: Int # Removed in the new schema
}A client sends this operation:
mutation SaveQuestion($input: SaveQuestionInput!) {
saveQuestion(input: $input) {
id
}
}The operation passes a SaveQuestionInput, so the change affects it. The operation text never mentions minEntries, so it's still valid, and the operation is potentially affected. After you publish the change, any request that includes minEntries in the $input variable fails with an error because the field no longer exists. Requests that don't include it keep working.
Example of a field changing from non-nullable to nullable
Suppose you change Product.name from String! to String. This operation still passes validation, so it's potentially affected:
query GetProduct($id: ID!) {
product(id: $id) {
name
}
}After you publish the change, name can be null. Clients that assume name always has a value — for example, because their generated types mark it as required — might throw errors or display incorrect data.
Example of an enum value removal
Suppose you remove CANCELED from this enum:
enum OrderStatus {
PENDING
SHIPPED
CANCELED # Removed in the new schema
}
type Query {
orders(status: OrderStatus): [Order]
}
type Order {
id: ID!
status: OrderStatus!
}This operation uses CANCELED directly in its text. It's no longer valid, so it's broken:
query CanceledOrders {
orders(status: CANCELED) {
id
}
}This operation passes the status in a variable and receives it in the response. It's still valid, so it's potentially affected:
query Orders($status: OrderStatus) {
orders(status: $status) {
id
status
}
}After you publish the change, requests that set $status to CANCELED fail. Client code that handles CANCELED orders in the response no longer receives them.
Resolving affected operations
To identify the specific changes that affect an operation, open the check in GraphOS Studio and select the operation. The Impacting Changes section lists each change. Hover over a change to see its change code, then find the code in the Types of schema changes section.
For broken operations:
Clients that send the operation receive errors as soon as you publish the change. Update clients that send the operation before you publish the change.
To remove or rename a schema element without breaking clients, make the change in stages. Add the new element, deprecate the old one, update clients to use the new element, and remove the old element after clients no longer use it. For details, go to Schema deprecations.
If the operation comes only from clients or client versions you no longer support, ignore the operation.
For potentially affected operations:
Select View Operation Body to see what parts of the operation use the changed schema elements.
Check the client code that sends the operation. Does it send the removed or changed input field or enum value in its variables? Does it depend on the old response type, nullability, or enum values? Does it rely on a default value that changed?
If you confirm that clients aren't affected, mark the changes as safe. Otherwise, update the clients before you publish the change. For details, go to Override changes per operation.
Enhanced operation checks
If you use GraphOS Router with extended reference reporting, GraphOS also records the enum values and input object fields each operation uses, including values sent in variables. Enhanced operation checks use that data to mark fewer operations as potentially affected.
With enhanced checks, the following changes are safe when usage data shows that clients can't be affected. If no other change affects the operation, it then becomes unaffected.
| Name | Enhanced checks treat the operation as unaffected when |
|---|---|
VALUE_REMOVED_FROM_ENUM | The operation never used the removed value, neither in the values clients sent nor in the responses they received. |
FIELD_REMOVED_FROM_INPUT_OBJECT | The removed field is nullable, and requests for the operation never set it. |
FIELD_ON_INPUT_OBJECT_CHANGED_TYPE | The field changed from nullable to non-nullable, and requests for the operation always sent a non-null value for it. |
For the input object changes in this table, enhanced checks don't change the status of broken operations. Enhanced checks don't change how any other change codes affect operations.
Enhanced checks also report two changes that standard checks don't: INPUT_OBJECT_FIELD_DEFAULT_VALUE_CHANGE and INPUT_OBJECT_FIELD_DEFAULT_VALUE_REMOVED. Standard checks never mark operations as affected by these changes. Enhanced checks mark an operation as affected if its requests omitted the field and therefore relied on the default value.
Enhanced checks are active only if every operation execution in the check's time window reported extended references. Any check whose time window includes traffic reported by Apollo Server, Apollo Gateway, or older Router versions turns off enhanced checks for any check whose time window includes that traffic. After you turn on extended reference reporting everywhere, enhanced checks activate once the time window no longer includes older traffic.
To see whether enhanced checks were active for a check, open the check's Details page and find the Enhanced operation checks section.
Types of schema changes
When you run a schema check, the check response classifies each schema change with these codes. In GraphOS Studio, hover over a change in an operation's Impacting Changes list to see its code.
Not every change to a schema is a potentially breaking change. Additive changes (such as adding a field to a type) are usually safe and don't affect active clients. Deletions and modifications (such as removing a field or changing a return type), however, can break clients that use affected types and fields.
If you use GraphOS Router to report operation metrics, you should enable extended reference reporting for more accurate schema checks. For more information, go to enhanced operation checks.
Potentially breaking changes
A potentially breaking change fails the check only if it affects at least one operation that isn't marked as safe or ignored.
Removals
These changes remove a schema element. Operations that use the removed element directly in their text are broken. Operations that use it only indirectly, such as through a variable or in response data, are potentially affected.
| Name | Description |
|---|---|
FIELD_REMOVED | A field was removed from a type. Operations that select the field are no longer valid. |
TYPE_REMOVED | A type was removed from the schema. Operations that name the type — for example, in a fragment (... on Book) or a variable definition ($filter: BookFilter) — are no longer valid. Operations that use the type only indirectly, such as a nested input type passed in a variable, are still valid but fail at runtime if clients send values for it. |
ARG_REMOVED | An argument was removed from a field. Operations that pass the argument are no longer valid. Operations that select the field without passing the argument are unaffected. |
TYPE_REMOVED_FROM_UNION | An object type was removed from a union. Operations that use a fragment on the removed type where the union is returned (for example, ... on Book inside a field that returns SearchResult) are no longer valid because the fragment can never match. Operations that don't use a fragment on the removed type are unaffected. |
FIELD_REMOVED_FROM_INPUT_OBJECT | A field was removed from an input object type. Operations that pass the input object as an argument are affected even if they don't set the removed field, because the check can't see values sent in variables. Those operations are still valid, but requests that include the removed field fail. With enhanced operation checks, removing a nullable field doesn't affect operations that never set it. |
VALUE_REMOVED_FROM_ENUM | A value was removed from an enum. Operations that use the removed value directly as an argument (for example, status: CANCELED) are no longer valid. Operations that pass the enum in a variable or receive it in a response are still valid, but requests that send the removed value fail, and clients no longer receive that value in responses. With enhanced operation checks, the removal doesn't affect operations that never used the removed value. |
TYPE_REMOVED_FROM_INTERFACE | An object type no longer implements an interface. Operations that use a fragment on the object type where the interface is returned are no longer valid because the fragment can never match. Operations that don't use a fragment on the object type are unaffected. |
Addition of required arguments
These changes add a required input to a schema element. Operations that don't provide the new input are no longer valid, or their requests fail at runtime.
| Name | Description |
|---|---|
REQUIRED_ARG_ADDED | A non-nullable argument without a default value was added to a field. Existing operations that select the field don't pass the new argument, so they're no longer valid. |
REQUIRED_FIELD_ADDED_TO_INPUT_OBJECT | A non-nullable field without a default value was added to an existing input object type. Operations that pass the input object as an argument are affected. If an operation writes the input object directly in its text, it's no longer valid. If it passes the input object in a variable, it's still valid, but requests fail until clients include the new field. |
In-place updates
These changes update an existing schema element. An affected operation is broken if it's no longer valid with the new type. Otherwise, it's potentially affected because clients might send values the new type rejects or receive data in a different shape.
| Name | Description |
|---|---|
FIELD_CHANGED_TYPE | A field's return type changed. Operations that select the field are affected. If the operation's selection no longer fits the new type (for example, it selects subfields of a type that's now a scalar), it's broken. Otherwise, clients receive data in a different shape. For example, a field changed from String! to String can return null, and a field changed from String to [String] returns a list. Changing a nullable type to the same non-nullable type (for example, String to String!) is non-breaking. |
FIELD_ON_INPUT_OBJECT_CHANGED_TYPE | The type of an input object field changed. Operations that pass the input object as an argument are affected. These operations are usually still valid, but requests fail if clients send a value that doesn't match the new type, for example null or no value after a change from Int to Int!. With enhanced operation checks, a change from nullable to non-nullable doesn't affect operations that always sent a non-null value. Changing a non-nullable type to the same nullable type (for example, Int! to Int) is non-breaking. |
TYPE_CHANGED_KIND | A type changed its "kind," for example from an object type to a union type. Operations that use the type are affected. If the operation no longer fits the new kind (for example, it selects fields directly on what's now a union), it's broken. Otherwise, it's potentially affected. |
ARG_CHANGED_TYPE | An argument's type changed. Operations that pass the argument are affected. Operations that pass the argument in a variable with the old type are no longer valid because the variable's type doesn't match the new argument type. Changing a non-nullable type to the same nullable type (for example, Int! to Int) is non-breaking. |
ARG_CHANGED_TYPE_OPTIONAL_TO_REQUIRED | An argument changed from an optional type to a required type (for example, Int to Int!). Operations that omit the argument, pass null, or pass a nullable variable are affected and usually no longer valid. Operations that always pass a non-null value are unaffected. |
Default values
These changes update the default value for an argument or input object field. Only operations that omit the value, and therefore rely on the default, are affected. These operations are usually still valid, but they might return different results after you publish the change.
| Name | Description |
|---|---|
ARG_DEFAULT_VALUE_CHANGE | An argument's default value changed or was removed. Operations that select the field without passing the argument are affected because they rely on the default. If the default was removed from a non-nullable argument, these operations are no longer valid. Adding a default value to an argument that didn't have one is non-breaking. |
INPUT_OBJECT_FIELD_DEFAULT_VALUE_CHANGE | An input object field's default value changed. Only enhanced operation checks report this change for operations; operations whose requests omitted the field, and therefore relied on the default, are affected. |
INPUT_OBJECT_FIELD_DEFAULT_VALUE_REMOVED | An input object field's default value was removed. Only enhanced operation checks report this change for operations: operations whose requests omitted the field, and therefore relied on the default, are affected. The ignore default value changes setting doesn't apply to removals. |
Non-breaking changes
These changes are detected by schema checks, but they are "safe." They never affect the behavior of any existing clients if deployed.
Schema additions
| Name | Description |
|---|---|
FIELD_ADDED | A field was added to an existing type. |
TYPE_ADDED | A type was added to the schema. |
VALUE_ADDED_TO_ENUM | A value was added to an enum. If clients contain a switch statement on the enum's value and do not include a default case, this change might cause unexpected behavior. |
TYPE_ADDED_TO_UNION | A type was added to a union used by at least one operation. |
TYPE_ADDED_TO_INTERFACE | An interface was applied to an object used by at least one operation. |
OPTIONAL_ARG_ADDED | A nullable argument, or a non-nullable argument with a default value, was added to an existing field. |
OPTIONAL_FIELD_ADDED_TO_INPUT_OBJECT | An optional field was added to an existing input object. |
INPUT_OBJECT_FIELD_DEFAULT_VALUE_ADDED | An existing input object field used by at least one operation had a default value added. |
Deprecations
| Name | Description |
|---|---|
FIELD_DEPRECATED | An existing field was deprecated. |
FIELD_DEPRECATION_REMOVED | A previously deprecated field is no longer deprecated. |
FIELD_DEPRECATED_REASON_CHANGE | The specified reason for a field's deprecation changed. |
ENUM_DEPRECATED | An existing enum value was deprecated. |
ENUM_DEPRECATION_REMOVED | A previously deprecated enum value is no longer deprecated. |
ENUM_DEPRECATED_REASON_CHANGE | The specified reason for an enum value's deprecation changed. |
Descriptions
| Name | Description |
|---|---|
TYPE_DESCRIPTION_CHANGE | An existing type's description changed. |
FIELD_DESCRIPTION_CHANGE | An existing field's description changed. |
ENUM_VALUE_DESCRIPTION_CHANGE | An existing enum value's description changed. |
ARG_DESCRIPTION_CHANGE | An existing argument's description changed. |