gql
is a collection of tools to manage GraphQL schema. The tool can be used for linting schema and finding breaking changes in the schema.
gql
can be installed with go install github.com/CrowdStrike/gql
~ $ gql -h
gql is a CLI built for federated GraphQL services' schemas
Usage:
gql [command]
Available Commands:
compare compare two graphql schemas
help Help about any command
lint lints given GraphQL schema
Flags:
-h, --help help for gql
Use "gql [command] --help" for more information about a command.
The linter is inspired from graphql-schema-linter with changes for supporting Apollo federation.
Help command prints help message. This has all the supported rules.
~ $ gql lint -h
lints given GraphQL schema
Usage:
gql lint [flags]
Flags:
-f, --filepath string Path to your GraphQL schema
-h, --help help for lint
-r, --rules strings Rules you want linter to use e.g.(-r type-desc,field-desc); available rules:
type-desc => type-desc checks whether all the types defined have description
args-desc => args-desc checks whether arguments have description
field-desc => field-desc checks whether fields have description
enum-caps => enum-caps checks whether Enum values are all UPPER_CASE
enum-desc => enum-desc checks whether Enum values have description
field-camel => field-camel checks whether fields defined are all camelCase
type-caps => type-caps checks whether types defined are Capitalized
relay-conn-type => relay-conn-type checks if Connection Types follow the Relay Cursor Connections Specification
relay-conn-args => relay-conn-args checks if Connection Args follow of the Relay Cursor Connections Specification
Specifying the schema file:
~ $ gql lint -f schema.graphqls
2022/05/28 12:53:01 16 errors occurred:
* 6:3 field Todo.id does not have description
* 7:3 field Todo.text does not have description
* 8:3 field Todo.done does not have description
* 9:3 field Todo.user does not have description
* 13:3 field User.id does not have description
* 14:3 field User.todos does not have description
* 14:9 argument User.todos.offset does not have description
* 14:21 argument User.todos.limir does not have description
* 15:3 field User.color does not have description
* 19:3 field Query.todos does not have description
* 19:9 argument Query.todos.offset does not have description
* 19:22 argument Query.todos.limit does not have description
* 23:3 field NewTodo.text does not have description
* 24:3 field NewTodo.userId does not have description
* 28:3 field Mutation.createTodo does not have description
* 28:14 argument Mutation.createTodo.input does not have description
Reading schema from stdin:
~ $ cat schema.graphqls | gql lint
2022/05/28 12:54:48 16 errors occurred:
* 6:3 field Todo.id does not have description
* 7:3 field Todo.text does not have description
* 8:3 field Todo.done does not have description
* 9:3 field Todo.user does not have description
* 13:3 field User.id does not have description
* 14:3 field User.todos does not have description
* 14:9 argument User.todos.offset does not have description
* 14:21 argument User.todos.limir does not have description
* 15:3 field User.color does not have description
* 19:3 field Query.todos does not have description
* 19:9 argument Query.todos.offset does not have description
* 19:22 argument Query.todos.limit does not have description
* 23:3 field NewTodo.text does not have description
* 24:3 field NewTodo.userId does not have description
* 28:3 field Mutation.createTodo does not have description
* 28:14 argument Mutation.createTodo.input does not have description
Passing subset of rules to be applied
~ $ gql lint -f schema.graphqls -r types-have-description
2022/05/28 13:23:20 1 error occurred:
* 3 errors occurred:
* 5:6 type Todo does not have description
* 22:7 type NewTodo does not have description
* 27:6 type Mutation does not have description
Specifying wildcards for schema file paths
graphql-linter -f '*.graphqls' -r types-have-description
2022/05/28 13:23:20 1 error occurred:
* 3 errors occurred:
* 5:6 type Todo does not have description
* 22:7 type NewTodo does not have description
* 27:6 type Mutation does not have description
Note: If your argument has wildcards your shell can execute the glob and provide individual values to graphql-linter. So don't forget the quotes around path with wildcards.
Following table describes all the lint rules supported by the linter
Lint Rule | Description |
---|---|
type-desc | type-desc checks whether all the types defined have description |
args-desc | args-desc checks whether arguments have description |
field-desc | field-desc checks whether fields have description |
enum-caps | enum-caps checks whether enum values are all UPPER_CASE |
enum-desc | enum-desc checks whether enum values have description |
field-camel | field-camel checks whether fields defined are all camelCase |
type-caps | type-caps checks whether types defined are Capitalized |
relay-conn-type | relay-conn-type checks whether types defined are following relay cursor connection spec |
relay-conn-args | relay-conn-args checks whether args defined are following relay cursor connection spec |
It is possible that you want certain part of your schema to be ignored for linting. You can do it with comments in your schema:
If you use #lint-disable rule1
on line x and #lint-enable rule1
for line y where x < y then errors because of rule1
between line x and y will be ignored from the output.
You can use #lint-disable-line rule1
to disable rule1 for a specific line. This only is applicable if rule1 is applied
for rest of the schema. If rule1 is not one among the rules passed then this has no effect on output.
compare command compares two schema files and returns all the differences. It is also built to support Apollo federation specification and can be used to find breaking changes in schema.
$gql compare -h
compare two graphql schemas
Usage:
gql compare [flags]
Flags:
-b, --breaking-change-only Get breaking change only
-e, --exclude-print-filepath Exclude printing schema filepath positions
-h, --help help for compare
-n, --newversion string Path to your new version of GraphQL schema
-o, --oldversion string Path to your older version of GraphQL schema
Compare the schema
~ $ gql compare -o oldSchema.graphql -n newSchema.graphql
❌ /Users/spahariya/code/temp/newSchema/library.graphql:2 Argument 'from' type changed from 'String' to 'String!' in directive '@transform'
❌ /Users/spahariya/code/temp/newSchema/library.graphql:20 Field 'Book.title' type changed from 'String!' to 'String' in OBJECT
❌ /Users/spahariya/code/temp/newSchema/library.graphql:18 Field 'Book.year' was removed from OBJECT
❌ /Users/spahariya/code/temp/newSchema/user.graphql:12 Input field 'UserInput.adBooks' type changed from '[Book]' to '[Book]!' in input object type
❌ /Users/spahariya/code/temp/newSchema/user.graphql:11 Input field 'UserInput.newBooks' type changed from '[Book]!' to '[Book!]!' in input object type
✋ /Users/spahariya/code/temp/newSchema/library.graphql:4 Member 'text' was added to Union type 'body'
✅ /Users/spahariya/code/temp/newSchema/library.graphql:19 Field 'Book.isbn' type changed from 'String' to 'String!' in OBJECT
✅ /Users/spahariya/code/temp/newSchema/library.graphql:15 Field 'Library.books' was added to OBJECT
✅ /Users/spahariya/code/temp/newSchema/library.graphql:8 Field 'Query.books' was added to OBJECT
✅ /Users/spahariya/code/temp/newSchema/user.graphql:6 Field 'User.address' was added to OBJECT
✅ /Users/spahariya/code/temp/newSchema/user.graphql:5 Field 'User.name' type changed from 'String' to 'String!' in OBJECT
Breaking errors in schema: 5
If user want to exclude schema filepath positions from the stdout, pass -e ot --exclude-print-filepath option with the command
~ $ gql compare -o "oldGraphql/*.graphql" -n "newGraphql/*.graphql" -e
❌ Field 'Book.isbn' type changed from 'String!' to 'String' in OBJECT
❌ Field 'Library.books' was removed from OBJECT
❌ Field 'Query.books' was removed from OBJECT
✋ Member 'text' was added to Union type 'body'
✅ Argument 'from' type changed from 'String!' to 'String' in directive '@transform'
✅ Field 'Book.title' type changed from 'String' to 'String!' in OBJECT
✅ Field 'Book.year' was added to OBJECT
Breaking errors in schema: 3
Generally speaking either a change can break API contract with client or it won't. But in case of GraphQL there's another category of changes, which won't actually break clients but will change their behavior and if not handled properly in code will cause client-side errors. Thus developers need to pay special attention to not only what are the breaking changes but also the dangerous ones. Following is the list of breaking and dangerous changes which can be made in graphql schema.
-
Breaking❌: Changes that will break existing queries to the GraphQL API. Below are the type of changes that comes in breaking change category.
- Schema root operation type (
Query
,Mutation
,Subscription
) changed or removed type
/field
/directive
/interface
removedtype
kind changeddirective
location changedfield
type changed or made optionalinput
type changed or made required or required fields added- argument type changed or removed or made required or required arguments added
enum
value removed- Union member removed
- Schema root operation type (
-
Dangerous✋ : Changes that won't break existing queries but could affect the runtime behavior of clients. Below are the type of changes that comes in dangerous change category.
- Argument default value changed
directive
optional argument added- Deprecation added/removed to
field
/enum
values enum
value added- Interfaces added to object implements
- Union member added
- Input fields added