ざっくり Apollo

Apollo のチュートリアルをやったので、そこから分かったことをざっくりまとめ

時間がある人は Apollo がとても丁寧にチュートリアルを作ってるので、実際に手を動かしてやるととても理解が深まります
https://www.apollographql.com/docs/tutorial/introduction/

## 動機

GraphQL とはなんぞやということが知りたかった

## 概要

### サーバーサイドでやること

GraphQL ではまず下記の三つを定義

- `スキーマ`(js のオブジェクト go の構造体みたいなの)
- `Query`(REST で言うとこの GET のメソッドたち)
- `Mutation`(REST で言うとこの POST, PUT, DELETE のメソッドたち)

DB(データソース)へのコネクトの conf 書いて
`Resolver`(GET でどんな処理をするかとか POST でどんな処理するかとか)を定義する

### クライアントサイドでやること

Apollo Client を使って GrapQL をかく
(他にもクライアントサイドでの state の管理とかできるけど今回は割愛)

## 詳細

### サーバーサイド

※チュートリアルで使ってた ApolloServer(すなわち node)前提

#### まず GraphQL で扱うスキーマを定義

```javascript
const { gql } = require("apollo-server");

const typeDefs = gql`
  type Anime {
    id: ID!
    title: String
    charactor: [Charactor]
  }

  type Charactor {
    id: ID!
    name: String
    age: String
    description: String
  }
`;
```

#### Query(REST でいうところの GET を定義)

`type Query`の中に GET のメソッド書く感じ

```javascript
const typeDefs = gql`
  # ↑のコードの続き

  type Query {
    animes: [Anime]
    anime(id: ID!): Anime
  }
`;
```

#### Mutation(REST でいうところの POST PUT DELETE を定義)

`type Mutation`の中にメソッド書く感じ
今回は Anime を delete するメソッドと Anime の title を更新するメソッドを追加

```javascript
const typeDefs = gql`
  # ↑のコードの続き

  # mutation実行時用のschemaを作る
  type AnimeUpdateResponse {
    success: Boolean!
    message: String
    anime: [Anime]
  }

  type Mutation {
    deleteAnime(animeId: ID!): AnimeUpdateResponse
    updateTitle(animeId: ID!, title: string!): AnimeUpdateResponse
  }
`;
```

##### Query の resolver の定義

今回の場合、Query で定義した animes と anime メソッドの処理内容

```javascript
module.exports = {
  Query: {
    animes: async (_, __, { dataSources }) =>
      await dataSources.animeAPI.getAllAnimes(),
    anime: async (_, { id }, { dataSources }) =>
      await dataSources.animeAPI.getAnimeById({ animeId: id }),
  },
};
```

`dataSources.animeAPI`が DB とのやり取りする adapter 的な感じ

こんな graphql がかけるようになる
GetAnimes は任意のクエリ名。

```graphql
query GetAnimes {
  animes() {
    id
    title
    charactor {
      name
      description
    }
  }
}
```

これはこんな感じのレスポンスを返してくれる
(イメージです。現実には違うかもしれません)

```json
{
  "data": {
    "animes": {
      "anime": [
        {
          "id": "1",
          "charactor": [
            {
              "name": "高須 竜児",
              "description": "目つきが悪い。家事がうまい。"
            },
            {
              "name": "逢坂 大河",
              "description": "元祖ツンデレ"
            }
          ]
        }
      ]
    }
  }
}
```

必要なカラムだけ指定すれば良きなの、とても便利ですよねぇ

##### Mutation の resolver の定義

今回の場合、anime の update と delete
面倒なので delete のみ

```javascript
module.exports = {
  // Query: {
  // ...
  // },
  Mutation: {
    deleteAnime: async (_, { id }, { dataSources }) => {
      const result = await dataSources.animeAPI.delete({ id });
      // AnimeUpdateResponseに合わせる
      if (!result)
        return {
          success: false,
          message: "faild delete anime",
        };

      const animes = await dataSources.animeAPI.getAllAnimes();
      return {
        success: true,
        message: "success delete anime",
        animes,
      };
    },
  },
};
```

こんな graphql がかける

```graphql
query DeleteAnimes {
  deleteAnime(id: 1) {
    success
    message
    animes {
      id
    }
  }
}
```

#### apollo server の起動

```javascript
const { ApolloServer } = require("apollo-server");
const typeDefs = require("./schema");
const { createStore } = require("./utils");
const resolvers = require("./resolvers");

// DBのコネクト
const store = createStore();

// DBのadapter的な
const AnimeAPI = require("./datasources/anime");

const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources: () => ({
    animeAPI: new AnimeAPI({ store }),
  }),
});

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});
```

### クライアントサイド

チュートリアルが React なので React で

#### ApolloClient の import

ApolloClient は ApolloProvider に渡す

```tsx
import { ApolloClient } from "apollo-client";
import { InMemoryCache, NormalizedCacheObject } from "apollo-cache-inmemory";
import { HttpLink } from "apollo-link-http";
import { ApolloProvider } from "@apollo/react-hooks";
import React from "react";
import ReactDOM from "react-dom";
import Pages from "./pages";

const cache = new InMemoryCache();
const link = new HttpLink({
  uri: "http://localhost:4000/",
});

const client: ApolloClient<NormalizedCacheObject> = new ApolloClient({
  cache,
  link,
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <Pages />
  </ApolloProvider>,
  document.getElementById("root")
);
```

#### GraphQL の実行

hooks(useQuery, useMutation) を使って grapql 実行する

```tsx
import React from "react";
import { useQuery } from "@apollo/react-hooks";
import gql from "graphql-tag";

export const GET_ANIMES = gql`
  query GetAnimes {
    animes() {
      id
      title
      charactor {
        name
        description
      }
    }
  }
`;

export const Animes: React.FC = () => {
  // GET_ANIMESで定義したqueryを実行する
  // <GetAnimes>はdataの型(apolloで自動生成される)
  const { data } = useQuery<GetAnimes>(GET_ANIMES);

  return (
    <Div>
      {data.animes.map((anime) => (
        <AnimeList title={anime.title} />
      ))}
    </Div>
  );
};
```

## 所感

大元のスキーマや Query、Mutation さえサーバサイド側で決めとけば
あとはよしなにクライアントサイド側で好きなスキーマで Query や Mutation かけるのは便利ぃ