LoginSignup
4
4

FlutterでGraphQLとgraphql_codegenを使ってみたので簡単にまとめてみた!

Last updated at Posted at 2024-05-17

GraphQL

今回は業務の中でGraphQLを使用する機会があったので、簡単にまとめてみました!

記事のターゲット

  • graphql_codegenを使ってみたい人
  • GraphQLの基礎を学びたい人

この記事に記載されていないこと

  • Fragmentについて(今後の記事で扱うかも)
  • 自動生成を行わないで実装する方法

GraphQLとは

GraphQLとは、APIを作成し、クライアントがサーバーからデータを取得するためのクエリ言語です。簡単に言えば、クライアントとサーバー間でデータを効率的にやり取りする方法です。

GraphQLとREST APIの違い

GraphQLとよく比較されるのがREST APIです。REST APIとGraphQLの主な違いは以下の通りです。

特徴 REST API GraphQL
エンドポイント 複数のエンドポイント 単一のエンドポイント
データ取得 固定されたレスポンス 必要なデータのみを指定して取得
データ操作 HTTPメソッド(GET, POST, PUT, DELETE) クエリとミューテーション
パフォーマンス 過剰なデータの送受信が発生する場合がある 必要なデータのみを取得し効率的
柔軟性 固定されたデータ構造 柔軟にデータ構造を指定可能

私がGraphQLを使用して最も良いと感じる点は、データ取得の際に必要なデータのみを指定して取得できることです。

例えば、ユーザー1とユーザー2を表示する画面で、ユーザーにはそれぞれ名前、生年月日、好きな食べ物が設定されているとします。このユーザーデータを取得する場合を考えます。

REST APIの場合:

ユーザー1とユーザー2の名前、生年月日、好きな食べ物の全てのデータを取得します。

GraphQLの場合:

ユーザー1は名前、生年月日、好きな食べ物の全てのデータを取得し、ユーザー2は名前と生年月日のみを指定して取得することが可能です。

GraphQLの場合はユーザー1とユーザー2で取得するデータをこちらから柔軟に指定できます:thumbsup:

GraphQLの基本概念

クエリ (Query)

クエリとは、クライアントがサーバーから必要なデータを取得するためのリクエストのことです。

例: あるアプリで、ユーザーのリストを表示する画面があるとします。この画面を表示するために、クライアント(アプリ側)はサーバーに「全てのユーザー情報をください」とリクエストを送ります。これがクエリです。

query GetUsers {
  users {
    id
    name
  }
}

ミューテーション (Mutation)

ミューテーションとは、サーバー上のデータを変更するための操作です。

例: あるアプリで、新しいユーザーを登録するフォームがあるとします。ユーザーがフォームに情報を入力して「登録」ボタンを押すと、クライアント(アプリ側)はサーバーに「新しいユーザーを追加してください」とリクエストを送ります。これがミューテーションです。

mutation AddUser($name: String!) {
  addUser(name: $name) {
    id
    name
  }
}

スキーマ (Schema)

スキーマとは、APIのデータ構造とそのデータがどのように操作されるかを定義するものです。

例: レストランのメニューがスキーマだとしましょう。メニューには、提供される料理の名前、価格、成分などが記載されています。これを見れば、どんな料理が注文できるのかがわかります。同じように、スキーマを見ると、そのAPIがどんなデータを提供し、どんな操作ができるのかがわかります。

type User {
  id: ID!
  name: String!
}

type Query {
  users: [User!]!
}

type Mutation {
  addUser(name: String!): User!
}

二つの型(input型とtype型)

type

type型は、サーバーからクライアントに返されるデータを定義します。つまり、クエリやミューテーションのレスポンスとして使用されます。例えば、ユーザー情報を返す場合の構造を定義します。

例:

type User {
  id: ID!
  name: String!
  email: String!
}

input

input型は、クライアントからサーバーに送信されるデータの構造を定義します。主にミューテーションの引数として使用されます。例えば、新しいユーザーを追加する際に送信するデータの構造を定義します。

例:

input CreateUserInput {
  name: String!
  email: String!
}

なぜ typeinput を区別する必要があるのか

GraphQLでは、typeinputを混在させないようにする必要があります。これにより、データの送受信時に構造が明確になり、予期しないエラーを防ぐことができます。

typeフィールドに他のtypeを参照することはできますが、inputフィールドにtypeを直接参照することはできません:weary:

間違った例とエラー例

初学者として、このルールを知らずに、inputタイプの中でtypeを参照しようとしてエラーになった例を示します。

input CreateUserInput {
  name: String!
  email: String!
  profile: UserProfile # inputにtypeを定義しているから間違い
}

type UserProfile {
  bio: String!
  avatarUrl: String!
}

上記のように、inputの中でtypeを参照することはできません。これを実行してしまうと、上手く通信ができなかったり、自動生成を使っている場合は生成に失敗します。
以下のようなエラーが表示されます。

[SEVERE] graphql_codegen on lib/data/graphql/schema.graphql:

Bad state: Failed to generate type for UserProfileInput.

正しい例

正しい方法は、inputの中でもinputタイプを使用することです。

input CreateUserInput {
  name: String!
  email: String!
  profile: UserProfileInput # これが正しい
}

input UserProfileInput {
  bio: String!
  avatarUrl: String!
}

これらの4つの概念を押さえておけば、GraphQLの基本は大丈夫です!

graphql_codegenで自動生成する方法

ステップ1: パッケージのインストール

まず、必要なパッケージをインストールします。

dependencies:
  flutter:
    sdk: flutter
  graphql_flutter: # GraphQLクライアント用のパッケージ

dev_dependencies:
  build_runner: # コード生成のためのパッケージ
  graphql_codegen: # GraphQLクエリやミューテーションの自動生成用

ステップ2: ディレクトリとファイルの作成

次に、lib/data/graphqlディレクトリを作成し、その中に以下のファイルを追加します。各ファイルの役割について簡単に説明します。

1. クエリファイル

lib/data/graphql/queries/get_user_query.graphql
query GetUsers {
  users {
    id
    name
  }
}

2. ミューテーションファイル

lib/data/graphql/mutations/add_user_mutation.graphql
mutation AddUser($input: CreateUserInput!) {
  addUser(input: $input) {
    id
    name
  }
}

3. スキーマファイル

GraphQLのスキーマを定義します。

lib/data/graphql/schema.graphql
type Query {
  users: [User!]!
  # !は必須、非nullの場合graphqlでは?は不要
  # []は配列
  # クエリが複数ある場合は、ここに追加して記載する。
}

type Mutation {
  addUser(input: CreateUserInput!): User!
  # ミューテーションが複数ある場合は、ここに追加して記載する。
}

# Userタイプの定義
type User {
  id: ID!
  name: String!
  email: String!
}

# 入力タイプの定義
input CreateUserInput {
  name: String!
  email: String!
  profile: UserProfileInput
}

input UserProfileInput {
  bio: String!
  avatarUrl: String!
}

ステップ3: build.yamlの設定

build.yamlファイルに以下の設定を追加して、コード生成を自動化します。これにより、指定したディレクトリ内のGraphQLファイルから自動的にDartコードが生成されます。

graphql_codegen:
  options:
    scopes:
      - lib/data/graphql/**
    clients:
      - graphql
      - graphql_flutter

/の後に **がないとファイルが認識されないので注意:eyes:

ステップ4: コード生成コマンドの実行

次に、コード生成コマンドを実行してDartコードを自動生成します。

flutter pub run build_runner build --delete-conflicting-outputs

自動生成されたコード

クエリ[GetUsers]
lib/data/graphql/queries/get_user_query.graphql.dart
import 'package:gql/ast.dart';

class Query$GetUsers {
  Query$GetUsers({
    required this.users,
    this.$__typename = 'Query',
  });

  factory Query$GetUsers.fromJson(Map<String, dynamic> json) {
    final l$users = json['users'];
    final l$$__typename = json['__typename'];
    return Query$GetUsers(
      users: (l$users as List<dynamic>)
          .map(
              (e) => Query$GetUsers$users.fromJson((e as Map<String, dynamic>)))
          .toList(),
      $__typename: (l$$__typename as String),
    );
  }

  final List<Query$GetUsers$users> users;

  final String $__typename;

  Map<String, dynamic> toJson() {
    final _resultData = <String, dynamic>{};
    final l$users = users;
    _resultData['users'] = l$users.map((e) => e.toJson()).toList();
    final l$$__typename = $__typename;
    _resultData['__typename'] = l$$__typename;
    return _resultData;
  }

  @override
  int get hashCode {
    final l$users = users;
    final l$$__typename = $__typename;
    return Object.hashAll([
      Object.hashAll(l$users.map((v) => v)),
      l$$__typename,
    ]);
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (!(other is Query$GetUsers) || runtimeType != other.runtimeType) {
      return false;
    }
    final l$users = users;
    final lOther$users = other.users;
    if (l$users.length != lOther$users.length) {
      return false;
    }
    for (int i = 0; i < l$users.length; i++) {
      final l$users$entry = l$users[i];
      final lOther$users$entry = lOther$users[i];
      if (l$users$entry != lOther$users$entry) {
        return false;
      }
    }
    final l$$__typename = $__typename;
    final lOther$$__typename = other.$__typename;
    if (l$$__typename != lOther$$__typename) {
      return false;
    }
    return true;
  }
}

extension UtilityExtension$Query$GetUsers on Query$GetUsers {
  CopyWith$Query$GetUsers<Query$GetUsers> get copyWith =>
      CopyWith$Query$GetUsers(
        this,
        (i) => i,
      );
}

abstract class CopyWith$Query$GetUsers<TRes> {
  factory CopyWith$Query$GetUsers(
    Query$GetUsers instance,
    TRes Function(Query$GetUsers) then,
  ) = _CopyWithImpl$Query$GetUsers;

  factory CopyWith$Query$GetUsers.stub(TRes res) =
      _CopyWithStubImpl$Query$GetUsers;

  TRes call({
    List<Query$GetUsers$users>? users,
    String? $__typename,
  });
  TRes users(
      Iterable<Query$GetUsers$users> Function(
              Iterable<CopyWith$Query$GetUsers$users<Query$GetUsers$users>>)
          _fn);
}

class _CopyWithImpl$Query$GetUsers<TRes>
    implements CopyWith$Query$GetUsers<TRes> {
  _CopyWithImpl$Query$GetUsers(
    this._instance,
    this._then,
  );

  final Query$GetUsers _instance;

  final TRes Function(Query$GetUsers) _then;

  static const _undefined = <dynamic, dynamic>{};

  TRes call({
    Object? users = _undefined,
    Object? $__typename = _undefined,
  }) =>
      _then(Query$GetUsers(
        users: users == _undefined || users == null
            ? _instance.users
            : (users as List<Query$GetUsers$users>),
        $__typename: $__typename == _undefined || $__typename == null
            ? _instance.$__typename
            : ($__typename as String),
      ));

  TRes users(
          Iterable<Query$GetUsers$users> Function(
                  Iterable<CopyWith$Query$GetUsers$users<Query$GetUsers$users>>)
              _fn) =>
      call(
          users: _fn(_instance.users.map((e) => CopyWith$Query$GetUsers$users(
                e,
                (i) => i,
              ))).toList());
}

class _CopyWithStubImpl$Query$GetUsers<TRes>
    implements CopyWith$Query$GetUsers<TRes> {
  _CopyWithStubImpl$Query$GetUsers(this._res);

  TRes _res;

  call({
    List<Query$GetUsers$users>? users,
    String? $__typename,
  }) =>
      _res;

  users(_fn) => _res;
}

const documentNodeQueryGetUsers = DocumentNode(definitions: [
  OperationDefinitionNode(
    type: OperationType.query,
    name: NameNode(value: 'GetUsers'),
    variableDefinitions: [],
    directives: [],
    selectionSet: SelectionSetNode(selections: [
      FieldNode(
        name: NameNode(value: 'users'),
        alias: null,
        arguments: [],
        directives: [],
        selectionSet: SelectionSetNode(selections: [
          FieldNode(
            name: NameNode(value: 'id'),
            alias: null,
            arguments: [],
            directives: [],
            selectionSet: null,
          ),
          FieldNode(
            name: NameNode(value: 'name'),
            alias: null,
            arguments: [],
            directives: [],
            selectionSet: null,
          ),
          FieldNode(
            name: NameNode(value: '__typename'),
            alias: null,
            arguments: [],
            directives: [],
            selectionSet: null,
          ),
        ]),
      ),
      FieldNode(
        name: NameNode(value: '__typename'),
        alias: null,
        arguments: [],
        directives: [],
        selectionSet: null,
      ),
    ]),
  ),
]);

class Query$GetUsers$users {
  Query$GetUsers$users({
    required this.id,
    required this.name,
    this.$__typename = 'User',
  });

  factory Query$GetUsers$users.fromJson(Map<String, dynamic> json) {
    final l$id = json['id'];
    final l$name = json['name'];
    final l$$__typename = json['__typename'];
    return Query$GetUsers$users(
      id: (l$id as String),
      name: (l$name as String),
      $__typename: (l$$__typename as String),
    );
  }

  final String id;

  final String name;

  final String $__typename;

  Map<String, dynamic> toJson() {
    final _resultData = <String, dynamic>{};
    final l$id = id;
    _resultData['id'] = l$id;
    final l$name = name;
    _resultData['name'] = l$name;
    final l$$__typename = $__typename;
    _resultData['__typename'] = l$$__typename;
    return _resultData;
  }

  @override
  int get hashCode {
    final l$id = id;
    final l$name = name;
    final l$$__typename = $__typename;
    return Object.hashAll([
      l$id,
      l$name,
      l$$__typename,
    ]);
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (!(other is Query$GetUsers$users) || runtimeType != other.runtimeType) {
      return false;
    }
    final l$id = id;
    final lOther$id = other.id;
    if (l$id != lOther$id) {
      return false;
    }
    final l$name = name;
    final lOther$name = other.name;
    if (l$name != lOther$name) {
      return false;
    }
    final l$$__typename = $__typename;
    final lOther$$__typename = other.$__typename;
    if (l$$__typename != lOther$$__typename) {
      return false;
    }
    return true;
  }
}

extension UtilityExtension$Query$GetUsers$users on Query$GetUsers$users {
  CopyWith$Query$GetUsers$users<Query$GetUsers$users> get copyWith =>
      CopyWith$Query$GetUsers$users(
        this,
        (i) => i,
      );
}

abstract class CopyWith$Query$GetUsers$users<TRes> {
  factory CopyWith$Query$GetUsers$users(
    Query$GetUsers$users instance,
    TRes Function(Query$GetUsers$users) then,
  ) = _CopyWithImpl$Query$GetUsers$users;

  factory CopyWith$Query$GetUsers$users.stub(TRes res) =
      _CopyWithStubImpl$Query$GetUsers$users;

  TRes call({
    String? id,
    String? name,
    String? $__typename,
  });
}

class _CopyWithImpl$Query$GetUsers$users<TRes>
    implements CopyWith$Query$GetUsers$users<TRes> {
  _CopyWithImpl$Query$GetUsers$users(
    this._instance,
    this._then,
  );

  final Query$GetUsers$users _instance;

  final TRes Function(Query$GetUsers$users) _then;

  static const _undefined = <dynamic, dynamic>{};

  TRes call({
    Object? id = _undefined,
    Object? name = _undefined,
    Object? $__typename = _undefined,
  }) =>
      _then(Query$GetUsers$users(
        id: id == _undefined || id == null ? _instance.id : (id as String),
        name: name == _undefined || name == null
            ? _instance.name
            : (name as String),
        $__typename: $__typename == _undefined || $__typename == null
            ? _instance.$__typename
            : ($__typename as String),
      ));
}

class _CopyWithStubImpl$Query$GetUsers$users<TRes>
    implements CopyWith$Query$GetUsers$users<TRes> {
  _CopyWithStubImpl$Query$GetUsers$users(this._res);

  TRes _res;

  call({
    String? id,
    String? name,
    String? $__typename,
  }) =>
      _res;
}

ミューテーション[AddUser]
lib/data/graphql/mutations/add_user_mutation.graphql.dart
import 'package:gql/ast.dart';

class Query$GetUsers {
  Query$GetUsers({
    required this.users,
    this.$__typename = 'Query',
  });

  factory Query$GetUsers.fromJson(Map<String, dynamic> json) {
    final l$users = json['users'];
    final l$$__typename = json['__typename'];
    return Query$GetUsers(
      users: (l$users as List<dynamic>)
          .map(
              (e) => Query$GetUsers$users.fromJson((e as Map<String, dynamic>)))
          .toList(),
      $__typename: (l$$__typename as String),
    );
  }

  final List<Query$GetUsers$users> users;

  final String $__typename;

  Map<String, dynamic> toJson() {
    final _resultData = <String, dynamic>{};
    final l$users = users;
    _resultData['users'] = l$users.map((e) => e.toJson()).toList();
    final l$$__typename = $__typename;
    _resultData['__typename'] = l$$__typename;
    return _resultData;
  }

  @override
  int get hashCode {
    final l$users = users;
    final l$$__typename = $__typename;
    return Object.hashAll([
      Object.hashAll(l$users.map((v) => v)),
      l$$__typename,
    ]);
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (!(other is Query$GetUsers) || runtimeType != other.runtimeType) {
      return false;
    }
    final l$users = users;
    final lOther$users = other.users;
    if (l$users.length != lOther$users.length) {
      return false;
    }
    for (int i = 0; i < l$users.length; i++) {
      final l$users$entry = l$users[i];
      final lOther$users$entry = lOther$users[i];
      if (l$users$entry != lOther$users$entry) {
        return false;
      }
    }
    final l$$__typename = $__typename;
    final lOther$$__typename = other.$__typename;
    if (l$$__typename != lOther$$__typename) {
      return false;
    }
    return true;
  }
}

extension UtilityExtension$Query$GetUsers on Query$GetUsers {
  CopyWith$Query$GetUsers<Query$GetUsers> get copyWith =>
      CopyWith$Query$GetUsers(
        this,
        (i) => i,
      );
}

abstract class CopyWith$Query$GetUsers<TRes> {
  factory CopyWith$Query$GetUsers(
    Query$GetUsers instance,
    TRes Function(Query$GetUsers) then,
  ) = _CopyWithImpl$Query$GetUsers;

  factory CopyWith$Query$GetUsers.stub(TRes res) =
      _CopyWithStubImpl$Query$GetUsers;

  TRes call({
    List<Query$GetUsers$users>? users,
    String? $__typename,
  });
  TRes users(
      Iterable<Query$GetUsers$users> Function(
              Iterable<CopyWith$Query$GetUsers$users<Query$GetUsers$users>>)
          _fn);
}

class _CopyWithImpl$Query$GetUsers<TRes>
    implements CopyWith$Query$GetUsers<TRes> {
  _CopyWithImpl$Query$GetUsers(
    this._instance,
    this._then,
  );

  final Query$GetUsers _instance;

  final TRes Function(Query$GetUsers) _then;

  static const _undefined = <dynamic, dynamic>{};

  TRes call({
    Object? users = _undefined,
    Object? $__typename = _undefined,
  }) =>
      _then(Query$GetUsers(
        users: users == _undefined || users == null
            ? _instance.users
            : (users as List<Query$GetUsers$users>),
        $__typename: $__typename == _undefined || $__typename == null
            ? _instance.$__typename
            : ($__typename as String),
      ));

  TRes users(
          Iterable<Query$GetUsers$users> Function(
                  Iterable<CopyWith$Query$GetUsers$users<Query$GetUsers$users>>)
              _fn) =>
      call(
          users: _fn(_instance.users.map((e) => CopyWith$Query$GetUsers$users(
                e,
                (i) => i,
              ))).toList());
}

class _CopyWithStubImpl$Query$GetUsers<TRes>
    implements CopyWith$Query$GetUsers<TRes> {
  _CopyWithStubImpl$Query$GetUsers(this._res);

  TRes _res;

  call({
    List<Query$GetUsers$users>? users,
    String? $__typename,
  }) =>
      _res;

  users(_fn) => _res;
}

const documentNodeQueryGetUsers = DocumentNode(definitions: [
  OperationDefinitionNode(
    type: OperationType.query,
    name: NameNode(value: 'GetUsers'),
    variableDefinitions: [],
    directives: [],
    selectionSet: SelectionSetNode(selections: [
      FieldNode(
        name: NameNode(value: 'users'),
        alias: null,
        arguments: [],
        directives: [],
        selectionSet: SelectionSetNode(selections: [
          FieldNode(
            name: NameNode(value: 'id'),
            alias: null,
            arguments: [],
            directives: [],
            selectionSet: null,
          ),
          FieldNode(
            name: NameNode(value: 'name'),
            alias: null,
            arguments: [],
            directives: [],
            selectionSet: null,
          ),
          FieldNode(
            name: NameNode(value: '__typename'),
            alias: null,
            arguments: [],
            directives: [],
            selectionSet: null,
          ),
        ]),
      ),
      FieldNode(
        name: NameNode(value: '__typename'),
        alias: null,
        arguments: [],
        directives: [],
        selectionSet: null,
      ),
    ]),
  ),
]);

class Query$GetUsers$users {
  Query$GetUsers$users({
    required this.id,
    required this.name,
    this.$__typename = 'User',
  });

  factory Query$GetUsers$users.fromJson(Map<String, dynamic> json) {
    final l$id = json['id'];
    final l$name = json['name'];
    final l$$__typename = json['__typename'];
    return Query$GetUsers$users(
      id: (l$id as String),
      name: (l$name as String),
      $__typename: (l$$__typename as String),
    );
  }

  final String id;

  final String name;

  final String $__typename;

  Map<String, dynamic> toJson() {
    final _resultData = <String, dynamic>{};
    final l$id = id;
    _resultData['id'] = l$id;
    final l$name = name;
    _resultData['name'] = l$name;
    final l$$__typename = $__typename;
    _resultData['__typename'] = l$$__typename;
    return _resultData;
  }

  @override
  int get hashCode {
    final l$id = id;
    final l$name = name;
    final l$$__typename = $__typename;
    return Object.hashAll([
      l$id,
      l$name,
      l$$__typename,
    ]);
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (!(other is Query$GetUsers$users) || runtimeType != other.runtimeType) {
      return false;
    }
    final l$id = id;
    final lOther$id = other.id;
    if (l$id != lOther$id) {
      return false;
    }
    final l$name = name;
    final lOther$name = other.name;
    if (l$name != lOther$name) {
      return false;
    }
    final l$$__typename = $__typename;
    final lOther$$__typename = other.$__typename;
    if (l$$__typename != lOther$$__typename) {
      return false;
    }
    return true;
  }
}

extension UtilityExtension$Query$GetUsers$users on Query$GetUsers$users {
  CopyWith$Query$GetUsers$users<Query$GetUsers$users> get copyWith =>
      CopyWith$Query$GetUsers$users(
        this,
        (i) => i,
      );
}

abstract class CopyWith$Query$GetUsers$users<TRes> {
  factory CopyWith$Query$GetUsers$users(
    Query$GetUsers$users instance,
    TRes Function(Query$GetUsers$users) then,
  ) = _CopyWithImpl$Query$GetUsers$users;

  factory CopyWith$Query$GetUsers$users.stub(TRes res) =
      _CopyWithStubImpl$Query$GetUsers$users;

  TRes call({
    String? id,
    String? name,
    String? $__typename,
  });
}

class _CopyWithImpl$Query$GetUsers$users<TRes>
    implements CopyWith$Query$GetUsers$users<TRes> {
  _CopyWithImpl$Query$GetUsers$users(
    this._instance,
    this._then,
  );

  final Query$GetUsers$users _instance;

  final TRes Function(Query$GetUsers$users) _then;

  static const _undefined = <dynamic, dynamic>{};

  TRes call({
    Object? id = _undefined,
    Object? name = _undefined,
    Object? $__typename = _undefined,
  }) =>
      _then(Query$GetUsers$users(
        id: id == _undefined || id == null ? _instance.id : (id as String),
        name: name == _undefined || name == null
            ? _instance.name
            : (name as String),
        $__typename: $__typename == _undefined || $__typename == null
            ? _instance.$__typename
            : ($__typename as String),
      ));
}

class _CopyWithStubImpl$Query$GetUsers$users<TRes>
    implements CopyWith$Query$GetUsers$users<TRes> {
  _CopyWithStubImpl$Query$GetUsers$users(this._res);

  TRes _res;

  call({
    String? id,
    String? name,
    String? $__typename,
  }) =>
      _res;
}

解説

クエリ[GetUsers]
サーバーからUserオブジェクトの配列を取得するリクエストを送信します。レスポンスには、idとnameフィールドを持つユーザーの配列が含まれています。
ミューテーション[AddUser]
新しいユーザーを作成するための入力データを受け取ります。レスポンスには、作成されたユーザーのidとnameが含まれています。
スキーマファイル
Userタイプを定義し、CreateUserInputという入力タイプを定義しています。この入力タイプは、新しいユーザーを作成するためのフィールドを持ち、ミューテーションの引数として使用されます。

これにより、GraphQLクエリとミューテーションのレスポンスがどのような形式になるかが明確になります。

graphql_codegenを使うメリット

graphql_codegenを使うことで、以下のようなメリットがあります。

1. 手動コーディングの削減

クエリやミューテーションを手動で書く手間を大幅に減らせます。自動生成されたコードを使用することで、構文エラーを防ぎ、効率的に開発を進めることができます。

2. 型安全性の向上

自動生成されたコードには型が明示的に定義されているため、手書きの場合は実際にAPIを叩いてみないとエラーが検出できなかったのが、コンパイル時にエラーを検出できます。これにより、ランタイムエラーを減らし、バグの少ないコードを作成することができます。

3. メンテナンスの容易さ

GraphQLスキーマが変更された場合でも、自動生成されたコードを再生成するだけで対応できます。これにより、コードのメンテナンスが容易になり、最新のスキーマに追従することができます。

これらのメリットを享受することで、効率的かつ高品質なGraphQLアプリケーションを開発することができます。

実際に使ってみた感想

自分はInput型Type型を理解していない状態で書いてしまい、自動生成が上手くできず大苦戦しました(笑)。
もし自分と同じく苦戦している方がいましたら、もう一度型の定義を見直してみてください
また今後もGraphQLについて新しい発見があれば、記事も更新していこうと思います。
最後まで読んでいただきありがとうございました🙇‍♂️

告知

最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを
募集しております。詳しくは採用情報ページをご確認ください。

みなさまからのご応募をお待ちしております。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4