Schema Checks Reference

Reference for types of schema changes that checks can detect


PLAN REQUIRED
This feature is available on the following GraphOS plans: Free, Developer, Standard, Enterprise.

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! to String don'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 never null might 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:

GraphQL
type Mutation {
  saveQuestion(input: SaveQuestionInput!): Question
}

input SaveQuestionInput {
  text: String!
  minEntries: Int # Removed in the new schema
}

A client sends this operation:

GraphQL
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:

GraphQL
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:

GraphQL
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:

GraphQL
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:

GraphQL
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.

note
Marking a change as safe or ignoring an operation only changes the check result; it doesn't prevent errors for clients that send a broken 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.

NameEnhanced checks treat the operation as unaffected when
VALUE_REMOVED_FROM_ENUMThe operation never used the removed value, neither in the values clients sent nor in the responses they received.
FIELD_REMOVED_FROM_INPUT_OBJECTThe removed field is nullable, and requests for the operation never set it.
FIELD_ON_INPUT_OBJECT_CHANGED_TYPEThe 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.

note
You can use ignored conditions settings to ignore all potentially breaking changes when detected on a variant without traffic.

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.

NameDescription
FIELD_REMOVEDA field was removed from a type. Operations that select the field are no longer valid.
TYPE_REMOVEDA 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_REMOVEDAn 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_UNIONAn 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_OBJECTA 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_ENUMA 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_INTERFACEAn 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.

NameDescription
REQUIRED_ARG_ADDEDA 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_OBJECTA 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.

note
In some cases, in-place updates are compatible with affected clients at runtime (such as a type rename or a conversion from an object to an interface that uses the same fields). However, schema checks still marks these as breaking changes, because validation does not have enough information to ensure that they are safe.
NameDescription
FIELD_CHANGED_TYPEA 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_TYPEThe 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_KINDA 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_TYPEAn 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_REQUIREDAn 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.

note
You can use ignored conditions settings to ignore default value changes. Default value removals can still be breaking changes.
NameDescription
ARG_DEFAULT_VALUE_CHANGEAn 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_CHANGEAn 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_REMOVEDAn 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

NameDescription
FIELD_ADDEDA field was added to an existing type.
TYPE_ADDEDA type was added to the schema.
VALUE_ADDED_TO_ENUMA 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_UNIONA type was added to a union used by at least one operation.
TYPE_ADDED_TO_INTERFACEAn interface was applied to an object used by at least one operation.
OPTIONAL_ARG_ADDEDA nullable argument, or a non-nullable argument with a default value, was added to an existing field.
OPTIONAL_FIELD_ADDED_TO_INPUT_OBJECTAn optional field was added to an existing input object.
INPUT_OBJECT_FIELD_DEFAULT_VALUE_ADDEDAn existing input object field used by at least one operation had a default value added.

Deprecations

NameDescription
FIELD_DEPRECATEDAn existing field was deprecated.
FIELD_DEPRECATION_REMOVEDA previously deprecated field is no longer deprecated.
FIELD_DEPRECATED_REASON_CHANGEThe specified reason for a field's deprecation changed.
ENUM_DEPRECATEDAn existing enum value was deprecated.
ENUM_DEPRECATION_REMOVEDA previously deprecated enum value is no longer deprecated.
ENUM_DEPRECATED_REASON_CHANGEThe specified reason for an enum value's deprecation changed.

Descriptions

NameDescription
TYPE_DESCRIPTION_CHANGEAn existing type's description changed.
FIELD_DESCRIPTION_CHANGEAn existing field's description changed.
ENUM_VALUE_DESCRIPTION_CHANGEAn existing enum value's description changed.
ARG_DESCRIPTION_CHANGEAn existing argument's description changed.