GraphQL+Lighthouse(+Laravel)でAPI開発1（インストール方法・設定編）

今回はLaravelで動作する既存システムに新規でAPIを追加することになり、GraphQLを採用することにしました。

GraphQLを採用した主な理由はページの特性上、REST形式で実装した場合APIコールの頻度が増えてしまうためです。全てのレスポンスを一つのエントリポイントに実装して、クエリパラメータでフィルタしていくこともできましたが、わざわざ複雑な実装をするよりはGraphQLでシンプルに実装することを選択しました。

という形式的な理由は置いておき、正直なところGraphQLを触ってみたかったのが一番大きな理由です。笑

記事は複数にまたがる予定で以下のような表題を予定しています。

1. [GraphQL+Lighthouse(+Laravel)でAPI開発1（インストール方法・設定編）](https://qiita.com/zizu21105/items/9e7312d41acf9fdc4226)
2. GraphQL+Lighthouse(+Laravel)でAPI開発2（開発編1 - 非Eloquentモデルの実装）
3. GraphQL+Lighthouse(+Laravel)でAPI開発3（開発編2 - エラーハンドリング）
4. GraphQL+Lighthouse(+Laravel)でAPI開発4（機能テスト編）

一応、インストール〜開発〜テストまでを一通り進めていったので、その過程で得た知見を残していこうと考えています。

# php開発用のgraphqlライブラリたち

LaravelにGraphQL用のアプリケーションを実装するためにはいくつかライブラリが用意されています。
GraphQLの公式サイトでは以下の二つが紹介されています。（2019/01/31現在）

- [graphql-php](https://github.com/webonyx/graphql-php)
- [graphql-relay-php](https://github.com/ivome/graphql-relay-php)

参考）[GraphQL公式サイトのライブラリ紹介](https://graphql.org/code/#php)

また、Laravel対応のものとしてはこちらも人気のようです。

- [laravel-graphql](https://github.com/Folkloreatelier/laravel-graphql)

しかし、今回は
- 型定義やスキーマの定義がGraphQLっぽい
- [ドキュメント](https://lighthouse-php.netlify.com/)が良い感じ
という理由から[Lighthouse](https://github.com/nuwave/lighthouse)を採用しました。
※lighthouseはgraphql-phpに依存しています。

# 構築手順
Laravelのローカル開発環境はこの手順で構築しました。
[Dockerで複数Laravel5.5プロジェクトの環境構築(1)](https://qiita.com/zizu21105/items/7456d317d15b09cab78b)
[Dockerで複数Laravel5.5プロジェクトの環境構築(2)](https://qiita.com/zizu21105/items/ecb865c6ab61376c6426)
Laravelの動作環境が整っている前提で話を進めていきます。


## 1. composer経由でlighthouse導入
```bash
composer require nuwave/lighthouse

# docker環境の場合
docker-compose run --rm hoge_composer require nuwave/lighthouse
```

## 2. schemaファイルの生成
```bash
php artisan vendor:publish --provider="Nuwave\Lighthouse\Providers\LighthouseServiceProvider" --tag=schema

# docker環境の場合
dc exec hoge php artisan vendor:publish --provider="Nuwave\Lighthouse\Providers\LighthouseServiceProvider" --tag=schema
```
すると、`routes/graphql/schema.graphql`という`Query`や`Mutation`などの実装例が記述されたファイルが生成されます。

## 3.デバッグツールのインストール
graphqlのクエリをチェックするためのデバッグツールがいくつか提供されています。代表的なものだと[grapiql](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en)などが有名です。今回はlighthouseの公式サイトで紹介されている[laravel-graphql-playground](https://github.com/mll-lab/laravel-graphql-playground)をインストールしましょう。

```bash
composer require --dev mll-lab/laravel-graphql-playground

# docker環境の場合
docker-compose run --rm hoge_composer require --dev mll-lab/laravel-graphql-playground
```

公式サイトではcomposerのrequireに入れるようにインストールしておりますが、本番環境に余計なものをいれないためにも`--dev`オプションを指定することにしました。

## 4. lighthouse用の設定ファイル作成
```bash
php artisan vendor:publish --provider="Nuwave\Lighthouse\Providers\LighthouseServiceProvider" --tag=config

# docker環境の場合
dc exec hoge php artisan vendor:publish --provider="Nuwave\Lighthouse\Providers\LighthouseServiceProvider" --tag=config
```
こちらのコマンドを実行すると`config/lighthouse.php`というファイルが生成されます。こちらのファイルはエントリポイントの変更やgraphqlエントリポイントのリクエストに対するミドルウェアの指定、カスタムエラーハンドラーの指定などが可能です。

基本的にはデフォルトの設定で問題ないと思います。
各項目にコメントを追記しておりますので、ご確認ください。

```php
<?php

use GraphQL\Error\Debug;
use GraphQL\Validator\Rules\DisableIntrospection;

return [
    /*
    |--------------------------------------------------------------------------
    | GraphQL endpoint
    |--------------------------------------------------------------------------
    |
    | Set the endpoint to which the GraphQL server responds.
    | The default route endpoint is "yourdomain.com/graphql".
    |
    */
    // xxxx.com/graphqlがエンドポイントとなる
    'route_name' => 'graphql', 

    /*
    |--------------------------------------------------------------------------
    | Enable GET requests
    |--------------------------------------------------------------------------
    |
    | This setting controls if GET requests to the GraphQL endpoint are allowed.
    |
    */
    // getリクエストを許容
    'route_enable_get' => true,

    /*
    |--------------------------------------------------------------------------
    | Route configuration
    |--------------------------------------------------------------------------
    |
    | Additional configuration for the route group.
    | Check options here https://lumen.laravel.com/docs/routing#route-groups
    |
    | Beware that middleware defined here runs before the GraphQL execution phase.
    | This means that errors will cause the whole query to abort and return a
    | response that is not spec-compliant. It is preferable to use directives
    | to add middleware to single fields in the schema.
    | Read more about this in the docs https://lighthouse-php.netlify.com/docs/auth.html#apply-auth-middleware
    |
    */
    'route' => [
        'prefix' => '',
        // middlewareのグループ指定
        // 上のコメントではこちらでミドルウェアを指定するとgraphqlの処理よりも前にミドルウェアが実行されるため、
        // エラーが発生した場合graphqlの仕様に沿ったレスポンスが返せないと警告しています。
        // 可能であればschemaファイル(schema.graphql)内で@middlewareディレクティブを用いて
        // クエリ毎にミドルウェアを指定してgraphql処理→ミドルウェアチェック
        // 順で実行することを推奨しているようです。
        // 'middleware' => ['loghttp']
    ],

    /*
    |--------------------------------------------------------------------------
    | Schema declaration
    |--------------------------------------------------------------------------
    |
    | This is a path that points to where your GraphQL schema is located
    | relative to the app path. You should define your entire GraphQL
    | schema in this file (additional files may be imported).
    |
    */
    'schema' => [
        'register' => base_path('routes/graphql/schema.graphql'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Schema Cache
    |--------------------------------------------------------------------------
    |
    | A large part of the Schema generation is parsing into an AST.
    | This operation is pretty expensive so it is recommended to enable
    | caching in production mode.
    |
    */
    'cache' => [
        'enable' => env('LIGHTHOUSE_CACHE_ENABLE', false),
        'key' => env('LIGHTHOUSE_CACHE_KEY', 'lighthouse-schema'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Directives
    |--------------------------------------------------------------------------
    |
    | List directories that will be scanned for custom server-side directives.
    |
    */
    'directives' => [__DIR__.'/../app/Http/GraphQL/Directives'],

    /*
    |--------------------------------------------------------------------------
    | Namespaces
    |--------------------------------------------------------------------------
    |
    | These are the default namespaces where Lighthouse looks for classes
    | that extend functionality of the schema.
    |
    */
    'namespaces' => [
        'models' => 'App\\Models',
        'queries' => 'App\\Http\\GraphQL\\Queries',
        'mutations' => 'App\\Http\\GraphQL\\Mutations',
        'interfaces' => 'App\\Http\\GraphQL\\Interfaces',
        'unions' => 'App\\Http\\GraphQL\\Unions',
        'scalars' => 'App\\Http\\GraphQL\\Scalars',
    ],

    /*
    |--------------------------------------------------------------------------
    | Security
    |--------------------------------------------------------------------------
    |
    | Control how Lighthouse handles security related query validation.
    | This configures the options from http://webonyx.github.io/graphql-php/security/
    | A setting of "0" means that the validation rule is disabled.
    |
    */
    'security' => [
        'max_query_complexity' => 0,
        'max_query_depth' => 0,
        'disable_introspection' => DisableIntrospection::DISABLED,
    ],

    /*
    |--------------------------------------------------------------------------
    | Debug
    |--------------------------------------------------------------------------
    |
    | Control the debug level as described in http://webonyx.github.io/graphql-php/error-handling/
    | Debugging is only applied if the global Laravel debug config is set to true.
    |
    */
    // debugモードの指定
    // こちらがonとなるとエラー時のstack traceなどの情報がエラーメッセージとして返されます。
    // 内部的にLaravelの.env内の環境変数APP_DEBUG=trueを見ていますので、本番環境では必ずfalseにしておきましょう。
    'debug' => Debug::INCLUDE_DEBUG_MESSAGE | Debug::INCLUDE_TRACE,

    /*
    |--------------------------------------------------------------------------
    | Error Handlers
    |--------------------------------------------------------------------------
    |
    | Register error handlers that receive the Errors that occur during execution and
    | handle them. You may use this to log, filter or format the errors.
    | The classes must implement Nuwave\Lighthouse\Execution\ErrorHandler
    |
    */
    // lighthouseのエラーハンドラーです。基本的にはこちらのエラーハンドラーで必要十分です。詳細はエラーハンドル編で記載します。
    'error_handlers' => [
        \Nuwave\Lighthouse\Execution\ExtensionErrorHandler::class,
    ],

    /*
    |--------------------------------------------------------------------------
    | Extensions
    |--------------------------------------------------------------------------
    |
    | Register extension classes that extend \Nuwave\Lighthouse\Schema\Extensions\GraphQLExtension
    |
    */
    'extensions' => [
        // \Nuwave\Lighthouse\Schema\Extensions\TracingExtension::class
    ],

     /*
     |--------------------------------------------------------------------------
     | GraphQL Controller
     |--------------------------------------------------------------------------
     |
     | Specify which controller (and method) you want to handle GraphQL requests.
     |
     */
    'controller' => 'Nuwave\Lighthouse\Support\Http\Controllers\GraphQLController@query',

    /*
    |--------------------------------------------------------------------------
    | Global ID
    |--------------------------------------------------------------------------
    |
    | The name that is used for the global id field on the Node interface.
    | When creating a Relay compliant server, this must be named "id".
    |
    */
    'global_id_field' => 'id',

    /*
    |--------------------------------------------------------------------------
    | Batched Queries
    |--------------------------------------------------------------------------
    |
    | GraphQL query batching means sending multiple queries to the server in one request,
    | You may set this flag to process/deny batched queries.
    |
     */
    'batched_queries' => true,
];
```
今回はマイページ内のAPIを実装したところ、既存システムで認証をmiddleware内で実装しているため全てのリクエストがユーザー認証のmiddlewareを通る必要がありました。そのため非推奨の方法ではありましたが、`lighthouse.php`の`'middleware' => 'mypage_middleware'`というmypage用のミドルウェアグループを指定しました。lighthouseでは`@middleware`ディレクティブを用いると基本的にはクエリに対するレスポンスを返す処理が先に実行され、その後ミドルウェアを実行しチェックするという実装のようです。


今回は以上で終了です。
次回は今回の環境構築から実際にgraphqlのサーバーサイド開発に着手した際の手順をご紹介します。