3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

今更ながら graphql-code-generator の便利さを痛感する その2

Last updated at Posted at 2020-10-07

はじめに

今日も今日とて、フロントReact + バックRailsの...

ということで、今回は今更ながら graphql-code-generator の便利さを痛感するの続きとして、登録、削除等のMutationの部分をやっていきます。

※構成は前回と同じです。

とりあえずApolloの公式に沿ってやってみる

まずはTODOを追加する仕組みを作っていきます。

追加用のテキストフィールドを用意しました。
ここに値を入力して、エンターで追加処理が実行される仕組みとします。

スクリーンショット 2020-09-22 12.44.46.png

src/App.tsx
+import { gql, useMutation } from "@apollo/client";

...

+const ADD_TODO = gql`
+  mutation addTodo($name: String!) {
+    addTodo(input: { name: $name }) {
+      todo {
+        id
+        name
+      }
+    }
+  }
+`;

 const App = () => {
   const { loading, data } = useTodosQuery();
+  const [addTodo] = useMutation(ADD_TODO);

...
         </p>
+        <input
+          type="text"
+          onKeyPress={(e) => {
+            if (e.key === "Enter") {
+              addTodo({ variables: { name: e.currentTarget.value } });
+            }
+          }}
+        />
         {loading ? (
...

文字を入力して、エンターで追加処理が実行できる仕組みができました。

エンター押下後、画面をリロードすると、TODOが追加されていることがわかります。

スクリーンショット 2020-09-22 14.18.50.png

リロードしないと動きがわからないので、ついでに、追加処理に加え、入力欄のクリア、一覧情報の再取得処理をやっておきます。

src/App.tsx
...
-  const { loading, data } = useTodosQuery();
+  const { loading, data, refetch } = useTodosQuery();
-  const [addTodo] = useMutation(ADD_TODO);
+  const [addTodo] = useMutation(ADD_TODO, {
+    update() {
+      refetch();
+    },
+  });

...

           onKeyPress={(e) => {
             if (e.key === "Enter") {
               addTodo({ variables: { name: e.currentTarget.value } });
+              e.currentTarget.value = "";
             }
           }}

特に型関連でエラーは起きませんでした。

もう少し便利になるよう作り込んでみる

何も入力せずに、登録処理を実行し、エラーが出るような仕組みを入れます。

Todoにバリデーションエラーのフィールドを追加して、以下のような値がレスポンスに含まれるようにしました。

"errors":[{"field":"name","error":"blank"}]

また、処理の結果が正常に行われたかのresultフィールド(Boolean)を追加しました。

resulttrueなら一覧をリロード、falseならエラー情報をalertで表示する仕組みを作ってみます。

src/App.tsx
 const ADD_TODO = gql`
   mutation addTodo($name: String!) {
     addTodo(input: { name: $name }) {
       todo {
         id
         name
+        errors {
+          field
+          error
+        }
      }
+      result
    }
  }
`;
src/App.tsx
   const [addTodo] = useMutation(ADD_TODO, {
-    update() {
-      refetch();
-    },
-  });
+    update(
+      _cache,
+      {
+        data: {
+          addTodo: {
+            todo: { errors },
+            result,
+          },
+        },
+      }
+    ) {
+      if (result) {
+        refetch();
+      } else {
+        errors.forEach(({ field, error }) => {
+          alert(`${field} ${error}`);
+        });
+      }
+    },
+  });

errorsの型が宣言されていないので、エラーが発生しました。

スクリーンショット 2020-09-22 16.10.28.png

型宣言をしてあげます。

type ValidationErrorType = {
  field: string;
  error: string;
};
-errors.forEach(({ field, error }) => {
+errors.forEach(({ field, error }: ValidationErrorType) => {
  alert(`${field} ${error}`);
});

エラーは解消され、バリデーションエラーメッセージがalertで確認できるようになりました。

スクリーンショット 2020-09-22 16.15.46.png

前回同様、graphql-code-generatorを使って、綺麗にしていこうと思います。

graphql-code-generatorを使う

まずは設定から

queriesとは別にmutations用のディレクトリを作成して、今回作成したクエリは、そちらに格納します。

codegen.yml
-documents: ./graphql/queries/*.graphql
+documents:
+  - ./graphql/mutations/*.graphql
+  - ./graphql/queries/*.graphql
graphql/mutations/add_todo.graphql
mutation addTodo($name: String!) {
  addTodo(input: { name: $name }) {
    todo {
      id
      name
      errors {
        field
        error
      }
    }
    result
  }
}

これでyarn generateを実行すると、src/types.d.tsにTODOを追加する用のuseAddTodoMutationが用意されました。

useAddTodoMutationを使って追加処理を書き直してみます。

-import { useTodosQuery } from "./types.d";
+import { useTodosQuery, useAddTodoMutation } from "./types.d";

+const [addTodo] = useMutation(ADD_TODO, {
+const [addTodo] = useAddTodoMutation({

ん?エラーが起きました。

スクリーンショット 2020-09-22 21.01.17.png

どうやらdatanullundefinedの可能性があるそうです。

それらを考慮して、少し書き直します。

const [addTodo] = useAddTodoMutation({
  update(_cache, { data }) {
    const result = data?.addTodo?.result || false;
    const errors = data?.addTodo?.todo.errors || [];

    if (result) {
      refetch();
    } else {
      errors.forEach((e) => {
        if (e) alert(`${e.field} ${e.error}`);
      });
    }
  },
});

少し手直しが必要でしたが、クエリと、型宣言の用意が不要になったので、ソース的にはスッキリしました :thumbsup:

同様に削除処理を実装してみる

雑ですが、各TODOの隣に、削除ボタンを用意して、削除処理を実行できるようにします。

スクリーンショット 2020-09-27 23.50.47.png

graphql/mutations/del_todo.graphql
mutation delTodo($id: ID!) {
  delTodo(input: { id: $id }) {
    todo {
      id
    }
  }
}

削除対象のTODOを特定するために、IDを取得する必要があります。

graphql/queries/todos.graphqlでは、TODOのnameだけ取得しているので、idもとるように修正します。

graphql/queries/todos.graphql
 query todos {
   todos {
+    id
     name
   }
 }

yarn generateを実行するとuseDelTodoMutation関数がsrc/types.d.tsに生まれました。

このuseDelTodoMutationを使って、削除ボタンクリック時に削除処理が実行できるようにします。

src/App.tsx
-import { useTodosQuery, useAddTodoMutation } from "./types.d";
+import { useTodosQuery, useAddTodoMutation, useDelTodoMutation } from "./types.d";


+const [delTodo] = useDelTodoMutation({
+  update() {
+    refetch();
+  }
+});


-{data && data.todos.map(({ name }, i) => <li key={i}>{name}</li>)}
+{data && data.todos.map(({ id, name }, i) => <li key={i}>{name}<button onClick={() => delTodo({ variables: { id } })}>削除</button></li>)}

お手軽に削除処理が実装できました :tada:

add-del.gif

最後に

一つの画面、コンポーネントで、一覧取得、追加処理、削除処理を実装したので、少しボリュームの大きめなファイルとなってしまったかもしれません。

別のファイルに型定義やクエリ情報を定義して、importでも良いかもしれないですが、人力で且つ、複数人となると、管理が次第に煩雑になったりする可能性があると思います。

これが、ある意味graphql-code-generatorのルールに則って開発をしていると、その問題が解消されるのではないかと思いました。

今回は、かなり小規模な例として、実装したので、導入にあたって障害となる箇所をあまり感じられませんでした。
LGTM なツールと思います :thumbsup:

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?