One of the promises of GraphQL is that it makes it easier to evolve your API on an ongoing basis in comparison to other approaches (like REST APIs). While API maintainers should still avoid making sudden breaking changes to supported types and fields, if you're building a client application it's nonetheless a good idea to make sure that all of the operations it uses can be safely made against the current iteration of the GraphQL API to which it's making its requests.
GitHub Actions provide a convenient way to hook into specific activities in a GitHub repository (for example, pushing or making a pull request) to ensure the GraphQL operations are valid at the time that event occurs. In this post, I'll outline how I built a custom GitHub Action to perform static analysis of all the GraphQL operations used by a client against a GraphQL schema. I'll also provide a demo of how to use this action in your workflows.
TL;DR: You can view the action repo here and a working demo of it in a workflow here.
Action Goals & Game Plan
I had the following high-level goals in mind when I created this action:
- Search for and validate all GraphQL operations in either
.graphql
files or in.js
files that contain thegql
template tag - Specify a directory in the project from which to begin recursively searching for operations (to narrow the search)
- Exclude directories or files from this search as needed
- Validate operations against a schema file co-located in the repository or a remote schema
- Send a token in an
Authorization
header when fetching the schema as needed
To accomplish these goals, I leaned heavily on the GraphQL.js and GraphQL Tools libraries. Specifically, GraphQL Tools provides functions for loading GraphQL operations from either code files (i.e. .js
files) or .graphql
files. It also provides functions for loading a schema from either a .graphql
file, a .json
file, or from an URL. The GraphQL.js library provides a function that validates the loaded operation documents against the loaded schema.
I then used the @actions/core package to put the pieces together and run the validation whenever the action is triggered and provide some feedback in the GitHub repository interface about the success or failure of the validation check, including what fields or types are invalid.
Using the Action in a Workflow
You'll first need to create a YAML file to describe the workflow in your project (for example, .github/workflows/operations.yml
).
Next, add a job to the file that uses the actions/checkout package to check out your repository's files (which adds them to the $GITHUB_WORKSPACE
). You can then run the validation action on the checked-out files as the job's second step.
I designed the GraphQL operation validation action to support the following inputs:
-
base_dir
(required): The base directory to search for operations (relative to repository's root directory) -
schema_location
(required): An endpoint URL or path of a.json
/.graphql
file (relative to repository's root directory) -
excluded_paths
: A comma-separated list of directory/file paths in thebase_dir
to exclude (relative tobase_dir
) -
token
: A token to use with an Authorization header (only the Bearer scheme is supported)
This example illustrates how you would validate the GraphQL operations whenever you push or make a pull request to the main
branch of a repository. The action points to a local schema file in the repository at swapi/schema.graphql
, looks for operations in the swapi
directory, and excludes the swapi/schema.graphql
file when searching for operations documents (because this would result in an error):
name: Validate GraphQL Operations
on:
pull_request:
push:
branches:
- main
jobs:
operations:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: Validate SWAPI operations
uses: mandiwise/graphql-operation-validation-action@v1
with:
schema_location: ${{ github.workspace }}/swapi/schema.graphql
base_dir: ${{ github.workspace }}/swapi
excluded_paths: schema.graphql
At a minimum, you must specify the ${{ github.workspace }}
as your base_dir
input when using actions/checkout, but you may wish to narrow the search to a specific subdirectory in the project that all operations are nested below. The node_modules
directory is automatically ignored when searching for operations, so you don't need to include it in the optional excluded_paths
input.
Now when you push to the main
branch or a PR is submitted you can check the Actions tab of your repository on GitHub to see the result. If any operations are invalid, then they will be noted in the log.
Send an Auth Token
If you need to validate your operations against a schema that requires a token in an Authorization
header, then you can add it as a token
input value:
# ...
- name: Validate GitHub GraphQL API operations
uses: mandiwise/graphql-operation-validation-action@v1
with:
schema_location: https://api.github.com/graphql
base_dir: ${{ github.workspace }}/github
token: ${{ secrets.ACCESS_TOKEN }}
The header will be sent using the Authorization: Bearer token...
scheme only. You can refer to GitHub's documentation on using encrypted secrets in a workflow to safely store your token with the client application's repository.
Run It Locally
Want to validate your operations in your development environment or without pushing to your repository? No problem!
You can use act to run this action locally. After installing act, you can use act push
to run this action on the previous workflow example in your local environment but without actually pushing any code to GitHub. This can be very handy for testing out changes to your GraphQL operations locally during ongoing development to your client application.
Good to Know
Because an introspection query is required to get the schema, introspection must be turned on for the GraphQL API you wish to validate your operations against. For this reason, the action is probably best-suited for use with a public GraphQL API, in your local development environment, or when the schema file is co-located with your client application.
Summary
This action was my first foray into GitHub Actions and I learned a lot throughout the process of developing it. GitHub Actions are very powerful and I found it a bit tricky at first to grok all of the moving parts and know where to start when creating a custom action, but with just a couple hundred lines of code, I hope this action provides a useful reference for you if you're interested in creating your own.
If you use this action in any of your GraphQL-related workflows I'd love to hear any feedback you have about it in the comments on this post or in the repo's Issues.
Top comments (0)