Schema Checks

The Schema Registry runs a suite of checks on a schema.

You can check a subgraph schema before publishing to a federated graph.

Run Schema Checks with the grafbase check command. Read more in the reference documentation.

A successful check returns a successful exit code (0).

Check errors return a non-zero exit code and print the errors, making the command suitable for scripts and CI pipelines.

When you run grafbase check, the Schema Registry:

  1. Gathers all subgraph schemas for the branch
  2. Validates each schema as a valid GraphQL schema
  3. Composes all subgraph schemas together with the new version of the checked subgraph

The Schema Check fails if errors occur in steps 2 or 3.

APIs evolve. Most changes add functionality: new mutations, fields on types, or input objects for filtering collections. You can make these changes safely without breaking API consumers.

Other changes break clients using queries written for previous API versions. Common examples include removing fields or adding required field arguments. These changes disrupt service because GraphQL servers reject invalid queries that don't match the current schema.

Operation Checks help prevent breaking changes to your GraphQL schema. They provide rules that surface breaking changes as errors when you run grafbase check.

Breaking changes must meet two criteria:

  • The change breaks existing functionality. See list of breaking changes.
  • Clients actively use the changed or removed schema parts.

Without the second criterion, Operation Checks would prevent schema iteration even for unused parts. Operation checks analyze request data to focus on actual API usage rather than theoretical breaking changes.

Configure Operation Checks in the dashboard as an opt-in feature.

Operation Checks configuration screen

Enable checks for your entire graph or specific branches. Select query thresholds and timeframes for schema usage analysis.

Exclude operations by name. For example, this GraphQL document names the operation "JapaneseProducts":

query JapaneseProducts { products(filter: { madeIn: "JP" }) { id name } }

Exclude specific clients by identifying them through the x-grafbase-client-name header. Include this header in any GraphQL client.

The grafbase check command runs Operation Checks with other schema checks. Errors indicate breaking changes that would occur when deploying the new schema. The system analyzes real client usage data from the target branch.

This list shows potentially breaking changes:

  • Removal of a root query, mutation or subscription type.
  • Removal of an object, interface or input object field.
  • Removal of a field argument.
  • Removal of an interface from implements.
  • Removal of a member type in a union.
  • Removal of a value of an enum.
  • Addition of a nonnullable argument on a field.
  • Addition of a nonnullable field on an input object.
  • Change of a field's type.
    • Either change of the inner type: String becoming Int for example,
    • or a change of wrapping types making the field nullable: String! to String or [String!] to [String] for example. The client would not expect null here.
  • Change of a field argument's type.
    • Either change of the inner type: String becoming Int for example,
    • or a change of wrapping types making the argument nonnullable: String to String! or [String] to [String!] for example. The client could be passing null.
  • Removal of a field argument's default, if the argument is required.

We check actual usage before returning Operation Check errors. For example, the removal of a field will only be breaking if a field has actually been used by clients in the configured time window.

Lint checks analyze your schema to find potential issues like mistakes, oversights or disallowed behaviors that don't cause hard compilation errors.

  • Naming conventions
    • Types: PascalCase
      • Forbidden prefixes: "Type"
      • Forbidden suffixes: "Type"
    • Fields: camelCase
    • Input values: camelCase
    • Arguments: camelCase
    • Directives: camelCase
    • Enums: PascalCase
      • Forbidden prefixes: "Enum"
      • Forbidden suffixes: "Enum"
    • Unions
      • Forbidden prefixes: "Union"
      • Forbidden suffixes: "Union"
    • Enum values: SCREAMING_SNAKE_CASE
    • Interfaces
      • Forbidden prefixes: "Interface"
      • Forbidden suffixes: "Interface"
    • Query fields
      • Forbidden prefixes: ["query", "get", "list"]
      • Forbidden suffixes: "Query"
    • Mutation fields
      • Forbidden prefixes: ["mutation", "put", "post", "patch"]
      • Forbidden suffixes: "Mutation"
    • Subscription fields
      • Forbidden prefixes: "subscription"
      • Forbidden suffixes: "Subscription"
  • Usage of the @deprecated directive requires specifying the reason argument
  • The CLI runs lint checks through grafbase check or in the dashboard after passing validation and composition checks
  • Run lint checks locally on SDL schemas with the grafbase lint command

Proposal checks enforce that all changes in the checked schema — compared to the currently published schema — are part of an approved schema proposal. The changes must not exactly match a given proposal, they only have to be part of any approved proposal. That means a given check or publish can implement in parts on in entirety one or more proposals. The check is based on semantic diffs between the checked schema and the relevant approved proposals.

Proposal checks are opt-in. You can enable them in the dashboard's Graph settings page.

$ grafbase check --schema products.graphql --name products grafbase/fed-demo@main Grafbase CLI 0.82.3 Checking... Errors were found in your schema check: Proposal checks [Error] No approved schema proposal contains the field `name` in the new object type `Seller`

Custom checks extend Grafbase's built-in schema validation capabilities with your own business logic and validation rules. They run alongside standard schema checks and can enforce organization-specific standards, domain-specific rules, or integrate with other systems to validate your GraphQL schemas.

The errors and warnings from custom checks are part of the schema check results, just like the results of built-in schema checks.

  • Enforcing naming conventions beyond built-in lint rules
  • Validating domain-specific constraints (e.g., certain fields must always appear together)
  • Ensuring compliance with your organization's API design guidelines
  • Integrating with other systems to validate business logic
  • Preventing anti-patterns specific to your implementation

Custom checks are implemented as webhooks that receive information about the subgraph schema being checked. You can build these webhooks using any language or framework and host them on your own infrastructure or serverless platforms.

To add a custom check, you must provide a webhook that will receive an event every time schema checks are run, and return an OK response with a potentially empty list of errors and warnings. These webhooks can be synchronous or asynchronnous.

Synchronous custom checks respond immediately with validation results. These are ideal for quick validations that don't require extensive processing.

The webhook will receive an event with following JSON body shape:

type SyncWebhookEvent = { graph_id: string branch_id: string git: { commit_url: string | null commit_message: string | null commit_sha: string | null branch_name: string | null author: string | null } subgraph: { name: string schema: string // full GraphQL schema being checked } }

The webhook must return a JSON response with a 200 HTTP status code and a body following this schema:

type SyncWebhookResponse = { diagnostics: Diagnostic[] } type Diagnostic = { message: string severity: "ERROR" | "WARNING" }

If the webhook fails to respond in less than 12 seconds, or it responds with a non-200 exit code, the custom check will be considered failing.

An empty diagnostics array corresponds to a successful check.

The errors and warnings will be part of the the check's results. Any failure to return a response with a 200 status and a valid body will also be an error in the check results, making the check fail.

const express = require('express'); const { parse, visit } = require('graphql'); const app = express(); app.use(express.json()); app.post('/custom-check', (req, res) => { const { subgraph } = req.body; const diagnostics = []; try { const parsedSchema = parse(subgraph.schema); const inputTypes = new Set(); const mutationFields = []; // Collect all input types visit(parsedSchema, { InputObjectTypeDefinition(node) { inputTypes.add(node.name.value); } }); // Check mutation fields visit(parsedSchema, { ObjectTypeDefinition(node) { if (node.name.value === 'Mutation') { node.fields.forEach(field => { mutationFields.push(field.name.value); // Check if there's a corresponding input type const expectedInputName = `${field.name.value}Input`; if (!inputTypes.has(expectedInputName)) { diagnostics.push({ message: `Mutation "${field.name.value}" should have a corresponding input type "${expectedInputName}"`, severity: "WARNING" }); } }); } } }); res.json({ diagnostics }); } catch (error) { res.status(500).json({ diagnostics: [{ message: `Failed to analyze schema: ${error.message}`, severity: "ERROR" }] }); } }); app.listen(3000, () => console.log('Custom check webhook running on port 3000'));

Asynchronous custom checks are designed for more complex validations that may take longer to process or require integration with other systems.

Coming soon: Asynchronous custom checks are currently in development. If you have specific use cases that would benefit from async custom checks, please contact us to become a design partner and help shape this feature.

Once you have implemented and exposed the webhook, you can enter its url in the Settings tab of your graph in the Grafbase dashboard:

After implementing your webhook:

  1. Navigate to the Settings tab of your graph in the Grafbase dashboard
  2. Find the "Custom Checks" section
  3. Enter your webhook URL
  4. Add any headers that your webhook expects

Custom Checks configuration in dashboard

View past schema checks in the Checks tab of the Grafbase dashboard.

Schema Checks tab in the dashboardd

If you use GitHub Actions for CI, there is a pre-packaged and documented grafbase-schema-check action that uses the same approach as the description below..

Use the command in scripts by providing the same arguments as interactive use.

Authentication differs from interactive use. Instead of the grafbase login flow, provide a Grafbase access token. Generate tokens in the dashboard:

Access tokens view in the dashboard

The grafbase introspect --dev command generates GraphQL schema files for the --schema argument of grafbase check. See examples in the GitHub workflow of the example repository. A federated graph example is also available.