Flutterでferryを使ってGraphQLクライアントを実装する

FlutterでAPIとしてGraphQLを利用する際の使用感を掴むために軽く調査したので、メモしておきます

# 使用するライブラリ

ドキュメントも作られていて、活発にメンテナンスされているようでしたので、**ferry**を採用しました

https://pub.dev/packages/ferry

https://ferrygraphql.com

他にも [graphql](https://pub.dev/packages/graphql)、[graphql_flutter](https://pub.dev/packages/graphql_flutter)、[artemis](https://pub.dev/packages/artemis) などが候補としてありましたが、記事執筆時点で、

- Stable版がNull-Safety対応していない
- フェッチしたデータの型付け処理のためのコードの自動生成に対応していない
- 動作が不安定っぽい

などの理由があったので、これらを利用するのはやめました

# 手順

## GraphQLのサーバー

**GraphQL Faker**を利用しました。

https://github.com/APIs-guru/graphql-faker

READMEに従ってサーバーを立てると、`http://localhost:9002/graphqh`にGraphQLのエンドポイントが作られます

## Flutterプロジェクト

### ferry の導入

Flutterプロジェクトに、以下コマンドにより必要なパッケージを追加します。

```shell
flutter pub add ferry
flutter pub add gql_http_link
flutter pub add -d ferry_generator
flutter pub add -d build_runner
```

### Dartコードの自動生成

GraphQL Faker でデフォルトで設定されているデータ構造のスキーマを `lib/graphql/schema.graphql` として保存します。

```graphql:lib/graphql/schema.graphql
type Company {
  id: ID
  name: String
  industry: String
  employees: [Employee!] @listLength(min: 5, max: 10)
}

type Employee {
  id: ID
  firstName: String
  lastName: String
  address: String
  subordinates: [Employee!] @listLength(min: 0, max: 3)
  company: Company
}

type Query {
  employee(id: ID): Employee
  company(id: ID): Company
  allCompanies: [Company!]
}
```

データ取得のスキーマを `lib/graphql/employee.query.graphql`　として作成します。

```graphql:lib/graphql/employee.query.graphql
query GetEmployees {
    employee {
        id
    }
}
```

ビルド設定ファイルを `build.yaml` として作成します。
以下はドキュメントの記述ほぼそのままです。
`your_package_name`はプロジェクト名に置き換えます。

```yaml:build.yaml
targets:
  $default:
    sources:
      - lib/**
    builders:
      gql_build|schema_builder:
        enabled: true
      gql_build|ast_builder:
        enabled: true
      gql_build|data_builder:
        enabled: true
        options:
          schema: your_package_name|lib/schema.graphql
      gql_build|var_builder:
        enabled: true
        options:
          schema: your_package_name|lib/schema.graphql
      gql_build|serializer_builder:
        enabled: true
        options:
          schema: your_package_name|lib/schema.graphql

      ferry_generator|req_builder:
        enabled: true
        options:
          schema: your_package_name|lib/schema.graphql
```

以下コマンドを実行し、GraphQL　クライアントのコードを自動生成します。

```shell
flutter pub run build_runner build
```

### API 呼び出しの実装

自動生成されたコードを利用して GraphQL の API を叩くクラスを、`lib/graphql.dart` として以下のように作成します。

```dart:lib/graphql.dart
class GraphQlAPIClient {
  late final Client _client;

  GraphQlAPIClient() {
    final link = HttpLink(
      'http://localhost:9002/graphql',
    );
    this._client = Client(link: link);
  }

  void listenEmployees() async {
    final request = GGetEmployeesReq();
    _client.request(request).listen((event) {
      final data = event.data;
      if (data != null) {
        print('Employee ID: ${data.employee?.id}');
      }
    });
  }
}
```

あとは、適当な場所から用意したメソッドを呼び出します。

```dart:lib/main.dart
void main() {
  final graphQlClient = GraphQlAPIClient();
  graphQlClient.listenEmployees();

  // ...
}
```

一点注意として、サーバーの向き先を`localhost`にしているので、シミュレーターやエミュレーターでしか動かないコードになっています。

# 参考
https://aakira.app/blog/2020/06/flutter-graphql/