Run Custom Schema Checks

Extend GraphOS schema checks with custom validations


This feature is only available with a GraphOS Enterprise plan. You can test it out by signing up for a free GraphOS trial. To compare GraphOS feature support across all plan types, see the pricing page.

Your organization's validation needs might extend beyond default schema checks. For example, you might want to enforce schema standards in addition to default GraphOS linter rules. You might require validations that depend on external data, such as ensuring schema changes are only submitted from a specific branch in version control. Custom schema checks let you supplement default checks by executing custom validations using your own services.

How custom checks work

Once you configure custom checks, GraphOS sends schema change information to an endpoint you specify. Your endpoint performs validations and reports potential issues back to GraphOS via the Platform API. GraphOS Studio displays the issues you report along with other check result details.

Schema check run showing custom check errors and warnings in GraphOS Studio

Custom checks run alongside other check types as part of every schema check run. Like operations, linter, and other check types, custom checks only run after a build check has successfully completed.

Prerequisites

Custom checks require using Rover CLI version 0.27.0 or later when you run schema checks.

Configuration

Custom checks require you to set up an HTTPS endpoint that's accessible via the public internet. You can set secret tokens to ensure that your endpoint only responds to requests sent by GraphOS. Once you register your endpoint in GraphOS Studio, GraphOS sends it webhook notifications as POST requests.

You can only register one endpoint for custom checks, but that endpoint can perform as many different schema validations as you want. You can write and deploy your validation service using any language or framework as long as the endpoint conforms to the expectations listed below.

Set up a validation endpoint

Your validation endpoint should:

  1. Be able to receive and process an HTTP POST request with a payload with the specified JSON format.

  2. Send a 200 response to the request.

    • When GraphOS dispatches a webhook with check information, it has a 30-second timeout to receive a 200 response.

    • If the request times out or doesn't receive a 200, it will retry up to five times, with an exponential backoff delay starting at 5 seconds and going up to 60 seconds.

  3. Submit validation check results to the GraphOS Platform API.

Outside of these requirements, you can implement any validations you need.

Webhook format

On every schema run, GraphOS sends your registered endpoint a POST request. The request has a JSON payload with the following shape:

JSON
1{
2  // Type of event, indicating a custom schema check in Apollo GraphOS
3  "eventType": "APOLLO_CUSTOM_CHECK",
4    // The unique identifier of the event
5  "eventId": "UUID",
6  // Version of the payload shape
7  "version": "1",
8  "checkStep": {
9    // The unique identifier of the graph being validated
10    "graphId": "string",
11    // The variant of the graph (for example, "current", "staging")
12    "graphVariant": "string",
13    // A unique identifier for the specific custom check task
14    "taskId": "UUID",
15    // A unique identifier for the workflow that initiated this check
16    "workflowId": "UUID",
17    "gitContext": {
18      // The branch from which the schema change is being submitted
19      "branch": "string",
20      // The commit hash associated with the schema change
21      "commit": "string",
22      // The name or email of the person who committed the change
23      "committer": "string",
24      // The commit message describing the schema change
25      "message": "string",
26      // The URL of the remote Git repository (for example, GitHub or GitLab)
27      "remoteUrl": "string"
28    }
29  },
30  "baseSchema": {
31    // The hash of the current base schema
32    "hash": "string",
33    // A list of subgraphs that make up the base schema (if using Apollo Federation)
34    "subgraphs": [
35      {
36        // The hash of the subgraph schema
37        "hash": "string",
38        // The name of the subgraph
39        "name": "string"
40      }
41    ]
42  },
43  "proposedSchema": {
44    // The hash of the proposed schema
45    "hash": "string",
46    // A list of subgraphs in the proposed schema (if using Apollo Federation)
47    "subgraphs": [
48      {
49        // The hash of the subgraph schema
50        "hash": "string",
51        // The name of the subgraph
52        "name": "string"
53      }
54    ]
55  }
56}
 note
The version attribute is included because payloads may include additional properties in the future.

Because schemas can be large, the payload provides schema hashes rather than full-text schemas. Your endpoint can request the full schema contents using the GraphOS Platform API. Refer to Platform API docs for instructions on making and authenticating requests. See the examples below for fetching schema contents by hash.

Using the schema contents and payload information, your endpoint can perform whatever validations you need.

Fetch a single schema

To fetch a single supergraph or subgraph schema contents by hash, use the following GraphQL query on the Platform API:

GraphQL
1query schema($graphId: ID!, $hash: SHA256) {
2  graph(id: $graphId) {
3    doc(hash: $hash) {
4      source
5    }
6  }
7}

Fetch all schemas

To request all subgraphs and supergraph schema in one request, use the following GraphQL query on the Platform API:

GraphQL
1query schemas($hashes: [SHA256!]!, $graphId: ID!) {
2  graph(id: $graphId) {
3    docs(hashes: $hashes) {
4      hash
5      source
6    }
7  }
8}

Submitting a check result

Once your endpoint has completed its validation, it should submit the results back to the triggering schema check run using the Platform API. The triggering schema check run waits for a response for ten minutes and doesn't complete until it receives it. If ten minutes pass without a response, the schema check run is marked as failed.

The results should include a list of violations, each with the following:

  • level: Violation severity level, either ERROR, WARN, or IGNORE

  • rule: Name of the violation type

  • message: Error message

Example check results mutation

You can use the following GraphQL mutation on the Platform API to submit custom check results:

GraphQL
1mutation SchemaCheckCallback(
2  $input: CustomCheckCallbackInput!
3  $name: String!
4  $graphId: ID!
5) {
6  graph(id: $graphId) {
7    variant(name: $name) {
8      customCheckCallback(input: $input) {
9        __typename
10        ... on CustomCheckResult {
11          violations {
12            level
13            message
14            rule
15          }
16        }
17        ... on PermissionError {
18          message
19        }
20        ... on TaskError {
21          message
22        }
23        ... on ValidationError {
24          message
25        }
26      }
27    }
28  }
29}

Enable custom checks in Studio

Once your endpoint is ready, you need to register it in GraphOS Studio.

  1. In GraphOS Studio, go to your graph's Checks page.

  2. Select Configuration in the upper right to open the checks configuration page.

  3. From the checks configuration page, open the Custom Checks section.

  4. Toggle on the switch next to Enable Custom Checks.

  5. Click the Edit button next to Registered webhook and enter the Endpoint URL.

  6. Optionally, enter a Secret Token.

    If you enter a token, each notification HTTP request includes an x-apollo-signature header whose value is a Hash Message Authentication Code (HMAC) generated using the token, the request body as the message, and the SHA256 hash function. The x-apollo-signature header has the format sha256=<hmac-value>.

    See an example
    Given the following inputs:Secret token (key): your_secret_tokenRequest body (message):
    JSON
    1{
    2  "eventType": "APOLLO_CUSTOM_CHECK",
    3  "eventId": "123e4567-e89b-12d3-a456-426614174000",
    4  "version": "1",
    5  "checkStep": {
    6    "graphId": "example-graph-id",
    7    "graphVariant": "staging",
    8    "taskId": "9f2a8b8e-e38f-4f4c-a2ad-7e50c2f55544",
    9    "workflowId": "a4b9d5c6-7b3e-40b9-a728-c98ef02e6e29",
    10    "gitContext": {
    11      "branch": "main",
    12      "commit": "f4d3c2a17e9b234bf9b1d1d8d0e5a678e5b1a9f0",
    13      "committer": "john.doe@example.com",
    14      "message": "Updated schema for new feature",
    15      "remoteUrl": "https://github.com/example/repo.git"
    16    }
    17  },
    18  "baseSchema": {
    19    "hash": "b1f3a79d2a73c8f6b56f5d42dbec4e9a6a7b1f00",
    20    "subgraphs": [
    21      {
    22        "hash": "f0a1b7c4d9e7f3a5c2b98e9a6f4d2c1b0e9f4a5b",
    23        "name": "users"
    24      }
    25    ]
    26  },
    27  "proposedSchema": {
    28    "hash": "c1b7a6f3d9e5f2b8c3a7e5b6c1d9e0f1b2a5f6a7",
    29    "subgraphs": [
    30      {
    31        "hash": "d9b7a5f2e3c9f8b4a1d2c7b5a3e4f6b2a1c7e5f9",
    32        "name": "orders"
    33      }
    34    ]
    35  }
    36}
    Hash function: SHA256The x-apollo-header value would be sha256=7b678bf54081f495866b5cecc5ada7ce85088dda2b46644a7f75ef823b9d5f86.

    Refer to this guide from Okta to learn more about implementation and see additional resources.

  7. You should now see your configured webhook on the checks configuration page. Click Send test notification to test your endpoint.

From now on, whenever you run schema checks, the check run will include a Custom Check task.

Custom check run in GraphOS Studio

Example validation service implementation

Refer to the example implementation for a custom check validation service that enforces GraphQL ESLint rules.

Feedback

Forums