Top View


Author Daiki Urata

eslint-plugin-graphql でGraphQLクエリのバリデーションを行う

2019/12/06

セットアップ

$ npm install --save-dev eslint eslint-plugin-graphql

設定ファイルに関しては各プロジェクトによって適したものを設定してください。今回はここの説明は省きます。(参考URL: https://eslint.org/docs/user-guide/getting-started)

GraphQLスキーマ

今回ESLintのベースとなるスキーマは以下になります。

type Query {
	todos: [Todo]
}

type Todo {
  id: ID!
  title: String
  body: String
  done: Boolean
}

このスキーマ情報をESLintへ設定を行い、クライアント側で使われているクエリが正しいものなのかチェックを行います。

スキーマ情報の取得

ESLintへスキーマ情報を読み込ませるにはGraphQLの Introspection 結果が書かれたjsonファイルが必要です。

このファイルを生成する方法の一つに Apollo CLI を使う方法があります。

Apollo CLI色々できるので他の機能も試してみるといいかもしれません。

まずはApollo CLIをグローバルにインストールします。

$ npm install -g apollo

インストールが完了したら、GraphQLサーバーのエンドポイントを指定してスキーマ情報が書かれたschema.jsonを生成します。

$ apollo client:download-schema schema.json --endpoint="[自分のGraphQLエンドポイント]"

すると、以下のようなschema.jsonファイルが出力されます。

{
  "__schema": {
    "queryType": {
      "name": "Query"
    },
    "mutationType": null,
    "subscriptionType": null,
    "types": [
      {
        "kind": "OBJECT",
        "name": "Query",
        "description": "",
        "fields": [
          {
            "name": "todos",
            "description": "",
            "args": [],
            "type": {
              "kind": "LIST",
              "name": null,
              "ofType": {
                "kind": "OBJECT",
                "name": "Todo",
                "ofType": null
              }
            },
            "isDeprecated": false,
            "deprecationReason": null
          }
        ],
        "inputFields": null,
        "interfaces": [],
        "enumValues": null,
        "possibleTypes": null
      },
 .
 .
 .
 }

eslint-plugin-graphqlの設定

GraphQLサーバーのスキーマ情報が取れたので、あとはESLintの設定を書くのみです。

クライアントアプリはReactやVueなどなんでもいいですが、リクエストに使用するクエリは別ファイルなどに切り出していることが多いと思います。

eslint-plugin-graphqlがサポートしている拡張子は .js、.gql、.graphqlとなっています。

今回でいうと以下のようなクエリになります。

// todos.js
import gql from 'graphql-tag'

export const GET_TODOS = gql`
  query GetTodos {
    todos {
      id
      title
    }
  }
`

このGET_TODOSをApollo Clientに読み込ませるなどしてサーバー側との通信を行うと思います。

それでは先ほど用意したschema.jsonを利用してESLintルールを記述します。

// .eslintrc.js
module.exports = {
   .
   .
   .
    rules: {
      "graphql/template-strings": ['error', {
        schemaJson: require('./schema.json')
      }],
      "graphql/named-operations": ['warn', {
        schemaJson: require('./schema.json'),
      }],
      "graphql/capitalized-type-name": ['warn', {
        schemaJson: require('./schema.json'),
      }]
    },
    plugins: [
      'graphql'
    ]
};

今回は上記のように設定しました。詳しい設定方法は公式リポジトリで確認できます。

基本的にgraphql/template-stringsを設定していれば良いですが、オプションで

  • graphql/named-operations: オペレーション名を必須にする
  • graphql/capitalized-type-name: 型名の最初は大文字にする

などが設定できます。今回はwarningを出すだけにしています。

次にeslintコマンド省略のためにpackage.jsonに設定します。

{
  .
  .
  "scripts": {
    .
    .
    "lint": "eslint . --ext .js"
  },
}

--extオプションでLint対象となるファイル拡張が指定できます。今回は.jsのみにしました。

試してみる

設定が完了したのでコマンドを実行してみます。

$ npm run lint

うまくいけばtodos.jsに書かれたクエリには問題ないのでエラーも出ずに終了します。

試しに誤ったクエリにtodos.jsを書きかえてみます。

// todos.js

import gql from 'graphql-tag'

export const GET_TODOS = gql`
  query {
    todos {
      id
      titlw
    }
  }
`

このようにオペレーション名をなくし、フィールド名も titletitlw にtypoした状態でLintしてみます。

$ npm run lint
/my-project/todos.js
  4:3  warning  All operations must be named                                      graphql/named-operations
  7:7  error    Cannot query field "titlw" on type "Todo". Did you mean "title"?  graphql/template-strings

それぞれerrorとwarningがちゃんとでました。

まとめ

ESLintを使ってクライアントアプリで書かれたクエリに対してのバリデーションチェックを行う設定を紹介しました。

これをCIでeslintコマンドを実行させたり、VSCodeなどのエディタでESLint拡張を導入して、事前にクエリエラーに気づけ、安全なGraphQL開発ライフを送ることができるようになりました。

しかし、これだけではMutationのネストされたInputタイプまではチェックすることはできません。

その場合はサーバー側を巻き込んでの統合テストや 前回の記事 で書いたようなモックサーバーを利用したテストを書くことをお勧めします。

Daiki Urata

Daiki Urata

Twitter X

フロントエンド/モバイルアプリなどを主に開発しています。