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:
- Gathers all subgraph schemas for the branch
- Validates each schema as a valid GraphQL schema
- 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.
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
becomingInt
for example, - or a change of wrapping types making the field nullable:
String!
toString
or[String!]
to[String]
for example. The client would not expect null here.
- Either change of the inner type:
- Change of a field argument's type.
- Either change of the inner type:
String
becomingInt
for example, - or a change of wrapping types making the argument nonnullable:
String
toString!
or[String]
to[String!]
for example. The client could be passing null.
- Either change of the inner type:
- 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"
- Forbidden prefixes:
- Fields:
camelCase
- Input values:
camelCase
- Arguments:
camelCase
- Directives:
camelCase
- Enums:
PascalCase
- Forbidden prefixes:
"Enum"
- Forbidden suffixes:
"Enum"
- Forbidden prefixes:
- Unions
- Forbidden prefixes:
"Union"
- Forbidden suffixes:
"Union"
- Forbidden prefixes:
- Enum values:
SCREAMING_SNAKE_CASE
- Interfaces
- Forbidden prefixes:
"Interface"
- Forbidden suffixes:
"Interface"
- Forbidden prefixes:
- Query fields
- Forbidden prefixes:
["query", "get", "list"]
- Forbidden suffixes:
"Query"
- Forbidden prefixes:
- Mutation fields
- Forbidden prefixes:
["mutation", "put", "post", "patch"]
- Forbidden suffixes:
"Mutation"
- Forbidden prefixes:
- Subscription fields
- Forbidden prefixes:
"subscription"
- Forbidden suffixes:
"Subscription"
- Forbidden prefixes:
- Types:
- Usage of the
@deprecated
directive requires specifying thereason
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:
- Navigate to the Settings tab of your graph in the Grafbase dashboard
- Find the "Custom Checks" section
- Enter your webhook URL
- Add any headers that your webhook expects
View past schema checks in the Checks
tab of the Grafbase dashboard.
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:
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.