【クライアントサイド編】GraphQLを使うぞ(TypeScript, Next.js使用)

※この記事は「<a href="https://qiita.com/koda-momo/items/ac66c8c965ee8d81d0c7">【サーバサイド編】GraphQLを使うぞ</a>」の続きになります。

こんにちは！今回はGraphQLを使ってTodoアプリを開発していきたいと思います。前回作成したサーバサイドと接続していく形です。(Next.jsの使い方には詳しく触れませんのでご了承ください。)

# 目次
[1.使用技術](#1-使用技術)
[2.使用する主なファイルの立位置](#2-使用する主なファイルの立位置)
[3.最初の準備](#3-最初の準備)
[4.データを全件取得](#4-データを全件取得)
[5.データの編集](#5-データの編集)
[6.データの1件取得](#6-データの1件取得)
[7.2つのメソッドを叩く](#7-2つのメソッドを叩く)
[8.型について](#8-型について)
[9.まとめ](#9-まとめ)


# 1. 使用技術
GraphQLではかなり細かく型が設定できるのでJavaScriptを使用しましたが、今回はTypeScriptを使用しました。
・Windows(10)
・言語:TypeScript
・フレームワーク:Next.js
・DB:MongoDB
・GraphQL(Nodeの中で使うイメージ)
・<a href="https://www.apollographql.com/docs/">Apollo Client</a>

# 2. 使用する主なファイルの立位置
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2365717/a5c8f9d6-29ff-cdca-6592-55ba26c0644d.png)


# 3. 最初の準備
### (1)Next.jsでプロジェクトを作成する
今回はTypeScriptを使用します。(ついでにnpmを使う設定も入れています)

```tex:コマンドプロンプト(ターミナル)
npx create-next-app client --typescript --use npm
```

### (2)クライアントサイドでGraphQLを使用するためインストール

```tex:コマンドプロンプト(ターミナル)
cd client
npm install @apollo/client graphql
```

### (3)サーバへの接続設定を書いていきます。

```react:_app.tsx
//インポート
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  useQuery,
  gql,
} from "@apollo/client";

//GraphQLのURL(エンドポイント)にする
const client = new ApolloClient({
  uri: "http://localhost:4000/graphql",
  cache: new InMemoryCache(),
});

function MyApp({ Component, pageProps }: AppProps) {

//ApolloProviderで囲むとその範囲でGraphQLが使えるようになる
  return (
    <Layout>
      <ApolloProvider client={client}>
        <Component {...pageProps} />
      </ApolloProvider>
    </Layout>
  );
}

export default MyApp;
```
今はサーバのURLがlocalhostですが、サーバ側をデプロイした後はここをそのURLに変えて、.envファイルとかに書いた方が良いかもです。

# 4. データを全件取得
### ■queryを書く
<a href="https://qiita.com/koda-momo/items/ac66c8c965ee8d81d0c7#9-%E4%B8%8A%E6%89%8B%E3%81%8F%E3%81%84%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B%E3%83%86%E3%82%B9%E3%83%88%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">前回のこの部分</a>で書いた、GraphiQLを基にquery文を書いていきます。
queries>query.tsを作成してその中に書いて下さい。

```tex:queryの書き方
export const 自分でつけるメソッド名 = gql`{クエリ文}`
```

```tex:queryの中の項目
 {
    サーバ側で決めたメソッド名 {
　　　　欲しいデータ
    }
  }
```

実際に全てのTodoを取得するクエリを書いてみると、下記のようになります。取得可能な項目やサーバ側で決めたメソッド名はGraphiQLを参考にして下さい。

```TypeScript:queries>query.ts
/**
 * Todoリスト全件取得.
 */
export const TODO_LIST = gql`
  {
    getAllTodo {
      id
      title
      finish
      category {
        id
        name
      }
    }
  }
`;
```

### ■データを取得して使う
取得してコンソールに出力してみます。書き方は下記の通りです。

```React:index.tsx
import type { NextPage } from "next";
import { useQuery } from "@apollo/client";
import { TODO_LIST } from "../queries/query";

const Home: NextPage = () => {
//useQuery(query.tsで名付けたメソッド名)
  const { loading, error, data } = useQuery(TODO_LIST);

  console.dir(JSON.stringify(data));

  //読み込み中時の表示
  if (loading) {
    return <p>loding…</p>;
  }
  //エラー時の表示
  if (error) {
    return <p>Error</p>;
  }

  return (<></>);
};

export default Home;

```
取得出来た！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2365717/1998483b-ffb0-d418-d111-3d086e8d733b.png)
後はいつも通り、データを画面に表示出来るよう整えていきます。

# 5. データの編集
次にデータの編集(Mutation)を書いていきます。今回はTodoリストにTodoを追加出来るようにしていきましょう。
### ■クエリを書く
データ取得の際と同じように、まずはクエリを書きます。
今回データ取得の際と異なる点は、()があってその中にごちゃごちゃ書いている点です。これはサーバ側作成時に指定した「args(引数)」に入る値になります。

```react:queries>query.ts
/**
 * Todoリスト追加.
 */
export const ADD_TODO = gql`
  mutation ($title: String, $categoryId: ID) {
    addTodo(title: $title, categoryId: $categoryId) {
      title
    }
  }
`;
```
Todoリスト追加する際に必要な情報は「新たなTodoのタイトル」と「カテゴリID」です。そのためその2つが入れられるようになっています。

```text
 mutation ($項目1の入力値: 項目1の型, $項目2の入力値: 項目2の型) {
    サーバ側で決めたメソッド名(項目1: $項目1の入力値, 項目2: $項目2の入力値) {
      追加後に返して欲しいデータ
    }
```

### ■データを編集する
それでは上記のqueryを使って、Todoを追加してみましょう。

```react:index.tsx
  /**
   * addTodo後データ再読み込み.
   */
  const [addTodo] = useMutation(ADD_TODO, {
    refetchQueries: [{ query: TODO_LIST }],
    awaitRefetchQueries: true,
  });

  /**
   * Todo追加.
   * @param data - 入力データ
   */
  const addTodoData = (data) => {
    addTodo({
      variables: {
        title: data.todoTitle,
        categoryId: data.categoryId,
      },
    });
  };
```

下の「Todo追加」が行われた後、上の「addTodo後データ再読み込み.」が動いてリアルタイムに画面上のデータの更新がなされます。Todoを追加した瞬間に画面上に新たなTodoが追加表示されるという訳です。では1つ1つのメソッドを見て行きます。

```text:index.tsx > addTodo後データ再読み込み
  const [下のメソッド名] = useMutation(queryで付けたメソッド名, {
    refetchQueries: [{ query: 再度取得の際に叩きたいクエリ }],
    awaitRefetchQueries: true,
  });
```

```text:index.tsx > Todo追加
  const メソッド名 = (入力データ) => {
    サーバ側で決めたメソッド名({
      variables: {
       送る値
      },
    });
  };
```
送る値の値が先ほど設定したqueryの「args」に入るという仕組みです。

データの更新、削除も同じように書くことが出来ます！

# 6. データの1件取得
これはデータの編集と同じように、取得ですが引数を渡す必要があります。引数がないとどのデータを持ってくればいいのか判断できないためです。

やり方はデータ取得の方法で引数を渡せるようにするだけです。

```typescript:queries > query.ts
/**
 * Todoリスト1件取得.
 */
export const TODO_ITEM = gql`
  query ($id: ID) {
    getTodo(id: $id) {
      title
      category {
        id
        name
      }
    }
  }
`;

```

```react:index.tsx
 /**
   * Todo1件取得.
   */
  const { loading, error, data } = useQuery(TODO_ITEM, {
    variables: { id: todoId },
  });
```

# 7. 2つのメソッドを叩く
GraphQLの良いところは、クライアントサイドで欲しいデータを選べることです。このようにサーバ側で設定したメソッドを複数一気に叩くことも出来ます。

ここではサーバ側で設定した「getTodo」というメソッドと「getAllCategory」というメソッドを同時に使ってデータを取得する「TODO_AND_CATEGRY」というメソッドを作成しています。

```react:queries > query.ts
/**
 * Todoリスト1件+カテゴリ全データ取得.
 */
export const TODO_AND_CATEGRY = gql`
  query ($id: ID) {
    getTodo(id: $id) {
      title
      category {
        id
        name
      }
    }
    getAllCategory {
      id
      name
    }
  }
`;
``` 

### ■引数が複数にわたる場合の例
ポイントは
・どの引数がどこに入るか区別出来るようにする事
・返ってきたデータを区別するために名前(今回はtodo1等)名前を付けてあげる事

```react
/**
 * Todoリストを同時に3件追加追加.
 */
export const ADD_THREE_TODO = gql`
  mutation (
    $title1: String
    $categoryId1: ID
    $title2: String
    $categoryId2: ID
    $title3: String
    $categoryId3: ID
  ) {
    todo1: addTodo(title: $title1, categoryId: $categoryId1) {
      title
    }
    todo2: addTodo(title: $title2, categoryId: $categoryId2) {
      title
    }
    todo3: addTodo(title: $title3, categoryId: $categoryId3) {
      title
    }
  }
`;
```

# 8. 型について
取得したデータの型はAPIでデータ取得の際と同じように、1つ1つ定義してあげます。

```typescript:types > type.ts
export type CategoryType = { id: string; name: string };

export type TodoType = {
  id: string;
  title: string;
  finish: boolean;
  category: CategoryType;
};
```

```react:index.tsx
const { loading, error, data } = useQuery(TODO_LIST);

const [dataArray, setDataArray] = useState<Array<TodoType>>([]);
setDataArray(data?.getAllTodo);
```


余談？ですが、GraphQLの型定義は「<a href="https://www.graphql-code-generator.com/">GraphQL Code Generator</a>」を使うと楽という事で、別のプロジェクトではこちらも少し使用してみました。コマンドを叩くだけで型が作成されるので楽ですが、若干クエリやJestでの書き方が変わる…？ようなので慣れるまでが少し大変そうでした。覚えてしまえば断然楽かと思います。

# 9. まとめ
今回はGraphQLを使ってTodoリストを作成しました。
まだまだ未熟な部分が多いので、何か気になる点や誤りがあればご連絡下さい。

ここまでご覧頂き、ありがとうございました。

#### おまけ:作成したTodoアプリのGitHub
https://github.com/koda-momo/graphql-todolist/tree/develop/todolist