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 で扱うスキーマを定義
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 のメソッド書く感じ
const typeDefs = gql`
# ↑のコードの続き
type Query {
animes: [Anime]
anime(id: ID!): Anime
}
`;
Mutation(REST でいうところの POST PUT DELETE を定義)
type Mutation
の中にメソッド書く感じ
今回は Anime を delete するメソッドと Anime の title を更新するメソッドを追加
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 メソッドの処理内容
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 は任意のクエリ名。
query GetAnimes {
animes() {
id
title
charactor {
name
description
}
}
}
これはこんな感じのレスポンスを返してくれる
(イメージです。現実には違うかもしれません)
{
"data": {
"animes": {
"anime": [
{
"id": "1",
"charactor": [
{
"name": "高須 竜児",
"description": "目つきが悪い。家事がうまい。"
},
{
"name": "逢坂 大河",
"description": "元祖ツンデレ"
}
]
}
]
}
}
}
必要なカラムだけ指定すれば良きなの、とても便利ですよねぇ
Mutation の resolver の定義
今回の場合、anime の update と delete
面倒なので delete のみ
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 がかける
query DeleteAnimes {
deleteAnime(id: 1) {
success
message
animes {
id
}
}
}
apollo server の起動
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 に渡す
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 実行する
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 かけるのは便利ぃ