Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/cjoudrey/graphql-schema-linter

Validate GraphQL schema definitions against a set of rules
https://github.com/cjoudrey/graphql-schema-linter

graphql linter

Last synced: 3 days ago
JSON representation

Validate GraphQL schema definitions against a set of rules

Awesome Lists containing this project

README

        

# graphql-schema-linter [![Travis CI](https://travis-ci.org/cjoudrey/graphql-schema-linter.svg?branch=master)](https://travis-ci.org/cjoudrey/graphql-schema-linter) [![npm version](https://badge.fury.io/js/graphql-schema-linter.svg)](https://yarnpkg.com/en/package/graphql-schema-linter)

This package provides a command line tool to validate GraphQL schema definitions against a set of rules.

![Screenshot](https://raw.githubusercontent.com/cjoudrey/graphql-schema-linter/master/screenshot-v0.0.24.png)

If you're looking to lint your GraphQL queries, check out this ESLint plugin: [apollographql/eslint-plugin-graphql](https://github.com/apollographql/eslint-plugin-graphql).

## Install

`graphql-schema-linter` depends on `graphql` as a peer dependency.

In order to use `graphql-schema-linter`, you can either add it to an existing project that uses the `graphql` package:

```
# Using yarn
yarn add graphql-schema-linter

# Using npm
npm install --save graphql-schema-linter
```

Or, you may install it globally along side `graphql`:

```
# Using yarn
yarn global add graphql-schema-linter graphql

# Using npm
npm install -g graphql-schema-linter graphql
```

## Usage

```
Usage: graphql-schema-linter [options] [schema.graphql ...]

Options:

-r, --rules

only the rules specified will be used to validate the schema

example: --rules fields-have-descriptions,types-have-descriptions

-o, --rules-options

configure the specified rules with the passed in configuration options

example: --rules-options '{"enum-values-sorted-alphabetically":{"sortOrder":"lexicographical"}}'

-i, --ignore

ignore errors for specific schema members (see "Inline rule overrides" for an alternative way to do this)

example: --ignore '{"fields-have-descriptions":["Obvious","Query.obvious","Query.something.obvious"]}'

-f, --format

choose the output format of the report

possible values: compact, json, text

-s, --stdin

schema definition will be read from STDIN instead of specified file

-c, --config-directory

path to begin searching for config files

-p, --custom-rule-paths

path to additional custom rules to be loaded. Example: rules/*.js

--comment-descriptions

use old way of defining descriptions in GraphQL SDL

--old-implements-syntax

use old way of defining implemented interfaces in GraphQL SDL

--version

output the version number

-h, --help

output usage information
```

### Usage with pre-commit Hooks

Using [lint-staged](https://github.com/okonet/lint-staged) and [husky](https://github.com/typicode/husky), you can lint
your staged GraphQL schema file before you commit. First, install these packages:

```bash
yarn add --dev lint-staged husky
```

Then add a `precommit` script and a `lint-staged` key to your `package.json` like so:

```json
{
"scripts": {
"precommit": "lint-staged"
},
"lint-staged": {
"*.graphql": ["graphql-schema-linter path/to/*.graphql"]
}
}
```

The above configuration assumes that you have either one `schema.graphql` file or multiple `.graphql` files that should
be concatenated together and linted as a whole.

If your project has `.graphql` query files and `.graphql` schema files, you'll likely need multiple entries in the
`lint-staged` object - one for queries and one for schema. For example:

```json
{
"scripts": {
"precommit": "lint-staged"
},
"lint-staged": {
"client/*.graphql": ["eslint . --ext .js --ext .gql --ext .graphql"],
"server/*.graphql": ["graphql-schema-linter server/*.graphql"]
}
}
```

If you have multiple schemas in the same folder, your `lint-staged` configuration will need to be more specific, otherwise
`graphql-schema-linter` will assume they are all parts of one schema. For example:

**Correct:**

```json
{
"scripts": {
"precommit": "lint-staged"
},
"lint-staged": {
"server/schema.public.graphql": ["graphql-schema-linter"],
"server/schema.private.graphql": ["graphql-schema-linter"]
}
}
```

**Incorrect (if you have multiple schemas):**

```json
{
"scripts": {
"precommit": "lint-staged"
},
"lint-staged": {
"server/*.graphql": ["graphql-schema-linter"]
}
}
```

## Configuration file

In addition to being able to configure `graphql-schema-linter` via command line options, it can also be configured via
one of the following configuration files.

For now, only `rules`, `schemaPaths`, `customRulePaths`, and `rulesOptions` can be configured in a configuration file, but more options may be added in the future.

### In `package.json`

```json
{
"graphql-schema-linter": {
"rules": ["enum-values-sorted-alphabetically"],
"schemaPaths": ["path/to/my/schema/files/**.graphql"],
"customRulePaths": ["path/to/my/custom/rules/*.js"],
"rulesOptions": {
"enum-values-sorted-alphabetically": { "sortOrder": "lexicographical" }
}
}
}
```

### In `.graphql-schema-linterrc`

```json
{
"rules": ["enum-values-sorted-alphabetically"],
"schemaPaths": ["path/to/my/schema/files/**.graphql"],
"customRulePaths": ["path/to/my/custom/rules/*.js"],
"rulesOptions": {
"enum-values-sorted-alphabetically": { "sortOrder": "lexicographical" }
}
}
```

### In `graphql-schema-linter.config.js`

```js
module.exports = {
rules: ['enum-values-sorted-alphabetically'],
schemaPaths: ['path/to/my/schema/files/**.graphql'],
customRulePaths: ['path/to/my/custom/rules/*.js'],
rulesOptions: {
'enum-values-sorted-alphabetically': { sortOrder: 'lexicographical' }
}
};
```

## Inline rule overrides

There could be cases where a linter rule is undesirable for a specific part of a GraphQL schema.

Rather than disable the rule for the entire schema, it is possible to disable it for that specific part of the schema using an inline configuration.

There are 4 different inline configurations:

- `lint-disable rule1, rule2, ..., ruleN` will disable the specified rules, starting at the line it is defined, and until the end of the file or until the rule is re-enabled by an inline configuration.

- `lint-enable rule1, rule2, ..., ruleN` will enable the specified rules, starting at the line it is defined, and until the end of the file or until the rule is disabled by an inline configuration.

- `lint-disable-line rule1, rule2, ..., ruleN` will disable the specified rules for the given line.

- `lint-enable-line rule1, rule2, ..., ruleN` will enable the specified rules for the given line.

One can use these inline configurations by adding them directly to the GraphQL schema as comments.

```graphql
# lint-disable types-have-descriptions, fields-have-descriptions
type Query {
field: String
}
# lint-enable types-have-descriptions, fields-have-descriptions

"""
Mutation root
"""
type Mutation {
"""
Field description
"""
field: String

field2: String # lint-disable-line fields-have-descriptions
}
```

**Note:** If you are authoring your GraphQL schema using a tool that prevents you from adding comments, you may use the `--ignore` to obtain the same functionality.

## Built-in rules

### `arguments-have-descriptions`

This rule will validate that all field arguments have a description.

### `defined-types-are-used`

This rule will validate that all defined types are used at least once in the schema.

### `deprecations-have-a-reason`

This rule will validate that all deprecations have a reason.

### `descriptions-are-capitalized`

This rule will validate that all descriptions, if present, start with a capital letter.

### `enum-values-all-caps`

This rule will validate that all enum values are capitalized.

### `enum-values-have-descriptions`

This rule will validate that all enum values have a description.

### `enum-values-sorted-alphabetically`

This rule will validate that all enum values are sorted alphabetically.

Accepts following rule options:

- `sortOrder`: `` - either `alphabetical` or `lexicographical`, defaults: `alphabetical`

### `fields-are-camel-cased`

This rule will validate that object type field and interface type field names are camel cased.

### `fields-have-descriptions`

This rule will validate that object type fields and interface type fields have a description.

### `input-object-fields-sorted-alphabetically`

This rule will validate that all input object fields are sorted alphabetically.

Accepts following rule options:

- `sortOrder`: `` - either `alphabetical` or `lexicographical`, defaults: `alphabetical`

### `input-object-values-are-camel-cased`

This rule will validate that input object value names are camel cased.

### `input-object-values-have-descriptions`

This rule will validate that input object values have a description.

### `interface-fields-sorted-alphabetically`

This rule will validate that all interface object fields are sorted alphabetically.

Accepts following rule options:

- `sortOrder`: `` - either `alphabetical` or `lexicographical`, defaults: `alphabetical`

### `relay-connection-types-spec`

This rule will validate the schema adheres to [section 2 (Connection Types)](https://facebook.github.io/relay/graphql/connections.htm#sec-Connection-Types) of the [Relay Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm).

More specifically:

- Only object type names may end in `Connection`. These object types are considered connection types.
- Connection types must have a `edges` field that returns a list type.
- Connection types must have a `pageInfo` field that returns a non-null `PageInfo` object.

### `relay-connection-arguments-spec`

This rule will validate the schema adheres to [section 4 (Arguments)](https://facebook.github.io/relay/graphql/connections.htm#sec-Arguments) of the [Relay Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm).

More specifically:

- A field that returns a `Connection` must include forward pagination arguments, backward pagination arguments, or both.
- To enable forward pagination, two arguments are required: `first: Int` and `after: *`.
- To enable backward pagination, two arguments are required: `last: Int` and `before: *`.

Note: If only forward pagination is enabled, the `first` argument can be specified as non-nullable (i.e., `Int!` instead of `Int`). Similarly, if only backward pagination is enabled, the `last` argument can be specified as non-nullable.

This rule will validate the schema adheres to [section 5 (PageInfo)](https://facebook.github.io/relay/graphql/connections.htm#sec-undefined.PageInfo) of the [Relay Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm).

More specifically:

- A GraphQL schema must have a `PageInfo` object type.
- `PageInfo` type must have a `hasNextPage: Boolean!` field.
- `PageInfo` type must have a `hasPreviousPage: Boolean!` field.

### `type-fields-sorted-alphabetically`

This rule will validate that all type object fields are sorted alphabetically.

Accepts following rule options:

- `sortOrder`: `` - either `alphabetical` or `lexicographical`, defaults: `alphabetical`

### `types-are-capitalized`

This rule will validate that interface types and object types have capitalized names.

### `types-have-descriptions`

This will will validate that interface types, object types, union types, scalar types, enum types and input types have descriptions.

## Output formatters

The format of the output can be controlled via the `--format` option.

The following formatters are currently available: `text`, `compact`, `json`.

### Text (default)

Sample output:

```
app/schema.graphql
5:1 The object type `QueryRoot` is missing a description. types-have-descriptions
6:3 The field `QueryRoot.songs` is missing a description. fields-have-descriptions

app/songs.graphql
1:1 The object type `Song` is missing a description. types-have-descriptions

3 errors detected
```

Each error is prefixed with the line number and column the error occurred on.

### Compact

Sample output:

```
app/schema.graphql:5:1 The object type `QueryRoot` is missing a description. (types-have-descriptions)
app/schema.graphql:6:3 The field `QueryRoot.a` is missing a description. (fields-have-descriptions)
app/songs.graphql:1:1 The object type `Song` is missing a description. (types-have-descriptions)
```

Each error is prefixed with the path, the line number and column the error occurred on.

### JSON

Sample output:

```json
{
"errors": [
{
"message": "The object type `QueryRoot` is missing a description.",
"location": {
"line": 5,
"column": 1,
"file": "schema.graphql"
},
"rule": "types-have-descriptions"
},
{
"message": "The field `QueryRoot.a` is missing a description.",
"location": {
"line": 6,
"column": 3,
"file": "schema.graphql"
},
"rule": "fields-have-descriptions"
}
]
}
```

## Exit codes

Verifying the exit code of the `graphql-schema-lint` process is a good way of programmatically knowing the
result of the validation.

If the process exits with `0` it means all rules passed.

If the process exits with `1` it means one or many rules failed. Information about these failures can be obtained by
reading the `stdout` and using the appropriate output formatter.

If the process exits with `2` it means an invalid configuration was provided. Information about this can be obtained by
reading the `stderr`.

If the process exits with `3` it means an uncaught error happened. This most likely means you found a bug.

## Customizing rules

`graphql-schema-linter` comes with a set of rules, but it's possible that it doesn't exactly match your expectations.

The `--rules ` allows you pick and choose what rules you want to use to validate your schema.

In some cases, you may want to write your own rules. `graphql-schema-linter` leverages [GraphQL.js' visitor.js](https://github.com/graphql/graphql-js/blob/6f151233defaaed93fe8a9b38fa809f22e0f5928/src/language/visitor.js#L138)
in order to validate a schema.

You may define custom rules by following the usage of [visitor.js](https://github.com/graphql/graphql-js/blob/6f151233defaaed93fe8a9b38fa809f22e0f5928/src/language/visitor.js#L138) and saving your newly created rule as a `.js` file.

You can then instruct `graphql-schema-linter` to include this rule using the `--custom-rule-paths ` option flag.

For sample rules, see the [`src/rules`](https://github.com/cjoudrey/graphql-schema-linter/tree/master/src/rules) folder of this repository or
GraphQL.js' [`src/validation/rules`](https://github.com/graphql/graphql-js/tree/6f151233defaaed93fe8a9b38fa809f22e0f5928/src/validation/rules) folder.