Amplify + AppSync + React + Typescriptで簡単アプリ作成【完成】

# 概要
前回の記事の続きを書いていきます。
前回の記事をみたい人は[こちら](https://qiita.com/keito1024/items/6c0980b253cb52fae6cc)

## クライアントからAPIを呼び出す
プロジェクト内でAppSyncを仕様するために, 提供されているライブラリを使用していきます。

```sh
$ yarn add aws-amplify aws-amplify-react
```
package.jsonを確認してインストールがされたことを確認してください。
確認できたら早速、エントリーポイントであるindex.tsxにインポートしましょう。

```tsx
import './index.css';

import Amplify from 'aws-amplify'; // <--- ライブラリインポート
import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';
import config from './aws-exports'; // <--- 追加
import * as serviceWorker from './serviceWorker';

// バックエンドの情報をAmplifyに渡してあげる
Amplify.configure(config);

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
serviceWorker.unregister();

```

## 一覧表示
最初の一歩として、一覧表示のAPIとつなぎ込んでみましょう。

```tsx
import './App.css';

import { API, graphqlOperation } from 'aws-amplify';
import React, { useEffect, useState } from 'react';

import { listTodos } from './graphql/queries';

type Todo = {
  id: string;
  name: string;
  description: string | null;
  createdAt: string;
  updatedAt: string;
};

const App: React.FC = () => {
  // Todoリスト
  const [posts, setPosts] = useState<Todo[]>([]);

  useEffect(() => {
    (async () => {
      // Todoの一覧取得
      const result = await API.graphql(graphqlOperation(listTodos));
      // graphqlOperationの内容によって戻り値が変わるのと、objectで特に型の指定もできないで型ガード入れてキャストしている
      if ("data" in result && result.data) {
        const posts = result.data as ListTodosQuery;
        if (posts.listTodos) {
          console.log(posts.listTodos);
          setPosts(posts.listTodos.items as Todo[]);
        }
      }
    })();
  }, []);
  return (
    <div className="App">
    </div>
  );
};

export default App;

```

まだamplifyで連携しているDB(DynamoDB)にデータがないので、空の配列が取得できると思います。
デベロッパーツールのコンソールを確認してみてください。
確認ができたら接続はできているかと思います。

## 登録
一覧表示してもデータがなければ意味がないので、新規追加APIともつなぎ込みます。

```tsx
import './App.css';

import { API, graphqlOperation } from 'aws-amplify';
import React, { useEffect, useState } from 'react';

import { ListTodosQuery, OnCreateTodoSubscription } from './api';
import { createTodo } from './graphql/mutations';
import { listTodos } from './graphql/queries';

type Todo = {
  id: string;
  name: string;
  description: string | null;
  createdAt: string;
  updatedAt: string;
};

const App: React.FC = () => {
  // Todoリスト
  const [posts, setPosts] = useState<Todo[]>([]);

  // Todo名
  const [name, setName] = useState("");

  // Todo内容
  const [description, setDescription] = useState("");

  useEffect(() => {
    (async () => {
      // Todoの一覧取得APIを呼ぶ
      const result = await API.graphql(graphqlOperation(listTodos));
      if ("data" in result && result.data) {
        const posts = result.data as ListTodosQuery;
        if (posts.listTodos) {
          setPosts(posts.listTodos.items as Todo[]);
        }
      }
    })();
  }, []);

  // Todoを新規追加
  const addTodo = async () => {
    if (!name || !description) {
      return;
    }
    // パラメタ
    const createTodoInput = {
      name,
      description,
    };

    try {
      //  Todoの新規追加APIを呼ぶ
      await API.graphql(
        graphqlOperation(createTodo, { input: createTodoInput })
      );
    } catch (error) {
      console.log(error);
    }
  };

  // Todo名の入力値をstateにセットする
  const handleChangeName = (e: React.ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  };

  // Todo内容の入力値をstateにセットする
  const handleChangeDescription = (e: React.ChangeEvent<HTMLInputElement>) => {
    setDescription(e.target.value);
  };

  return (
    <div className="App">
      <div>
        Todo名
        <input value={name} onChange={handleChangeName} />
      </div>
      <div>
        Todo内容
        <input value={description} onChange={handleChangeDescription} />
      </div>
      <button onClick={addTodo}>Todo追加</button>
    </div>
  );
};

export default App;


```
実際にフォームからTodoを追加してみましょう。
追加したらDynamoDBにレコードが追加されていることを確認してください。

そこまでできたら、あとは追加したTodoをリアルタイムで表示したいですよね。

## サブスクリプション(購読)
Todoの追加をトリガーにリアルタイムで画面を更新した内容で描画するようにしてみます。

```tsx
import './App.css';

import { API, graphqlOperation } from 'aws-amplify';
import React, { useEffect, useState } from 'react';

import { ListTodosQuery, OnCreateTodoSubscription } from './api';
import { createTodo } from './graphql/mutations';
import { listTodos } from './graphql/queries';
import { onCreateTodo } from './graphql/subscriptions';

type PostSubscriptionEvent = { value: { data: OnCreateTodoSubscription } };
type Todo = {
  id: string;
  name: string;
  description: string | null;
  createdAt: string;
  updatedAt: string;
};

const App: React.FC = () => {
  // Todoリスト
  const [posts, setPosts] = useState<Todo[]>([]);

  // Todo名
  const [name, setName] = useState("");

  // Todo内容
  const [description, setDescription] = useState("");

  useEffect(() => {
    (async () => {
      // Todoの一覧取得APIを呼ぶ
      const result = await API.graphql(graphqlOperation(listTodos));
      if ("data" in result && result.data) {
        const posts = result.data as ListTodosQuery;
        if (posts.listTodos) {
          setPosts(posts.listTodos.items as Todo[]);
        }
      }

      // 新規追加イベントの購読
      const client = API.graphql(graphqlOperation(onCreateTodo));
      if ("subscribe" in client) {
        client.subscribe({
          next: ({ value: { data } }: PostSubscriptionEvent) => {
            if (data.onCreateTodo) {
              const post: Todo = data.onCreateTodo;
              setPosts((prev) => [...prev, post]);
            }
          },
        });
      }
    })();
  }, []);

  // Todoを新規追加
  const addTodo = async () => {
    if (!name || !description) {
      return;
    }
    // パラメタ
    const createTodoInput = {
      name,
      description,
    };

    try {
      //  Todoの新規追加APIを呼ぶ
      await API.graphql(
        graphqlOperation(createTodo, { input: createTodoInput })
      );
    } catch (error) {
      console.log(error);
    }
  };

  // Todo名の入力値をstateにセットする
  const handleChangeName = (e: React.ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  }; 

  // Todo内容の入力値をstateにセットする
  const handleChangeDescription = (e: React.ChangeEvent<HTMLInputElement>) => {
    setDescription(e.target.value);
  };

  return (
    <div className="App">
      <div>
        Todo名
        <input value={name} onChange={handleChangeName} />
      </div>
      <div>
        Todo内容
        <input value={description} onChange={handleChangeDescription} />
      </div>
      <button onClick={addTodo}>追加</button>
      <div>
        {posts.map((data) => {
          return (
            <div key={data.id}>
              <h4>{data.name}</h4>
              <p>{data.description}</p>
            </div>
          );
        })}
      </div>
    </div>
  );
};

export default App;
```

追加アクションの監視を実装しました。
マウント時に一覧取得、新規追加の購読を行い、Todoが追加されると新規で追加したTodoが表示している一覧に追加され表示される流れです。
実際にアプリケーションを起動して試してみてください。

### 最後に
記事だけでみると一見難しそうな感じはしていましたが、いざ触ってみるとこんな簡単にGraphQLのAPIが作れてしまうのは驚きました。
今回はDynamoDBがメインで連携していましたが、他にもlambdaやcognitoとも連携ができるので時間あるときに試してみようかなと思います。