はじめに
本記事では、GraphQL 導入時に Vite / Vue / Apollo / GraphQL Code Generator を使った GraphQL モックサーバ環境の作り方の一例を説明します。
利用するツール/技術要素について
Vite ⚡️
Vite は、Vue の開発者である Evan You さんが中心となって開発したフロントエンドのビルドツールです。Vue の開発者が作ったものですが、Vue 以外のフレームワークを適用することも可能です。Vite の dev-server は、ES Module で動作するため、バンドル処理しないことから、起動やHot Module Replacement (HMR) が高速です。
余談ですが、Vite はフランス語で quick
という意味だそうです。Vue もフランス語で view
という意味なので、フランス語で命名する傾向があるのかもしれません。
Vue
Vue は、フロントエンドのフレームワークです。賛否両論あるかと思いますが、単一ファイルコンポーネントとして、HTML と Javascript を分けやすいので、React と比較してデザインのプロトタイピングなどがやりやすい印象です。Vueの記法を覚えると、Javascript が得意でなくても簡単なものは作れます。最近では、Vue composition API という React の Hooks のような機能も追加されています。
今回は検討の前提が Vue だったため、Vue を利用します。
Apollo
Apollo は、GraphQL によるアプリ/サービス間のコミュニケーションを簡単にするための吸収層をプラットフォームとして提供しています。プラットフォームとして色々提供されているようですが、その中でも今回は以下を使用します。
Apollo Client
Apollo Client は、フロントエンドで、GraphQL Query Client として利用します。キャッシュ機構や状態管理を肩代わりしてくれることが、Apollo Client を利用する主なモチベーションです。Apollo 以外にも、Vue で利用できる GraphQL Query Client として、uqrl や SWRV、Villus などがありますが、今回は一番メジャーな Apollo を使用します。
Apollo Server
Apollo Server は、フロントエンドで Apollo を利用するのでセットで採用しました。笑
モックサーバーで GraphQL のバックエンド処理 (リゾルバー) を書くのは大変ですが、Apollo Server はモックモードがあり、クエリに対する単純な結果を返してくれます。ただし、フィルタやページングのような制御はモックモードでは確認できません。
Apollo Studio
Apollo Studio を起動すると、ブラウザ上で Apollo Studio により、スキーマ定義やエクスプローラーでクエリ実行結果を確認できます。そのため、フロントエンド開発時にスキーマの検証に使えることはもちろん、フロントエンド側で GraphQL スキーマを設計する場合に、バックエンド側にスキーマやクエリを共有しやすいかなと思います。
GraphQL
GraphQL は、クエリ言語仕様です。Meta 社 (旧: Facebook) が仕様 として明確に定義しています。フロントエンドの表示に重きを置いており、クエリとクエリ実行結果が対になっていることが特徴です。Facebook はもちろん、他の有名どころだと Shopify や Github など採用されており、REST と異なり、API 利用者側が必要なものをクエリとして定義して柔軟に結果を取得できます。
GraphQL Code Generator
GraphQL Code Generator は、GraphQL スキーマを Typescript のコードに変換するためのツールです。フロントエンド開発時に GraphQL スキーマの型はそのまま使えないので、このツールを使って自動生成した型を利用して開発します。
Apollo でも Code Generator が存在するようですが、GraphQL Code Generator の方が機能が豊富・安定しているようなので、採用しました。GraphQL Code Generator で生成したコードを GraphQL Client の SDK としても利用できるようですが、キャッシュなどのハンドリングは自前になりそうなので、そこは Apollo と棲み分けて利用します。
モックサーバー開発環境
環境構築の流れを以下に書きますが、不要な方は、vue-graphql-demo に構築結果を置いていますので、見ていただければと思います。
前提
今回は、Node のパッケージマネージャーとして yarn を使います。(npm を使う方は適宜読み替えてください)
ディレクトリ構成
最終的には、以下のようなディレクトリ構成になります。
.
├── README.md
├── backendmock # モックサーバー
│ ├── README.md
│ ├── node_modules
│ ├── nodemon.json
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── resolvers
│ │ └── utils
│ ├── tsconfig.json
│ └── yarn.lock
└── frontend # フロントエンド
├── README.md
├── codegen.yml
├── graphql.schema.json
├── index.html
├── node_modules
├── package.json
├── public
├── schema.graphql
├── src
│ ├── App.vue
│ ├── assets
│ ├── components
│ ├── env.d.ts
│ ├── generated
│ └── main.ts
├── tsconfig.json
├── vite.config.ts
└── yarn.lock
環境構築
適当にディレクトリを掘る
$ mkdir vue-graphql-demo
$ cd vue-graphql-demo
フロントエンド
Vite プロジェクトの作成
Viteのガイドを参考にプロジェクトを作ります。
$ yarn create vite frontend --template vue-ts
$ cd frontend
$ yarn
この時点で、 yarn run dev
を実行すると Vite プロジェクトの起動が確認できます。
次に GraphQL と Apollo Client をインストールします。Apollo Client は、Vue3 に対応した apollo-composable を使用します。
GraphQL + Apollo Client のインストール
$ yarn add graphql graphql-tag @apollo/client @vue/apollo-composable
main.ts
は、以下のようにすることで、 Vue コンポーネント内で Apollo を使えるようになります。
import { createApp, provide, h } from 'vue';
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core';
import { DefaultApolloClient } from '@vue/apollo-composable';
import App from './App.vue';
const httpLink = createHttpLink({ uri: 'http://localhost:4000' }); // 後で構築するモックサーバーのエンドポイントを指定
const cache = new InMemoryCache();
const apolloClient = new ApolloClient({ link: httpLink, cache });
const app = createApp({
setup () {
provide(DefaultApolloClient, apolloClient)
},
render: () => h(App)
});
app.mount('#app');
GraphQL Code Generator のインストール
以下で、GraphQL Code Generator で利用するモジュールをインストールします。各モジュールの説明は今回は割愛します。
$ yarn add -D @graphql-codegen/cli @graphql-codegen/introspection @graphql-codegen/typescript @graphql-codegen/typescript-document-nodes @graphql-codegen/typescript-graphql-request @graphql-codegen/typescript-operations
GraphQL Code Generator は、以下のように定義します。
overwrite: true
schema: schema.graphql # GraphQL スキーマの置き場所
generates:
src/generated/graphql.ts: # 自動生成結果格納先
plugins:
- typescript
- typescript-operations
- typescript-graphql-request
- typescript-document-nodes
- fragment-matcher
./graphql.schema.json:
plugins:
- "introspection"
# 自動生成時に結果をきれいにしたい場合は、Prettier / Eslint などをインストールした上で Hooks に登録
# hooks:
# afterAllFileWrite:
# - prettier --write
# - eslint --fix
コード生成するためのコマンドを package.json
に追加しておきます。以下の例では、yarn run gql:gen
を実行することで、GraphQL スキーマからコード生成ができるようになります。
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"gql:gen": "graphql-codegen --config codegen.yml" // 追加
}
これで、GraphQL 含めたフロントエンド環境が、yarn run dev
で起動できるようになりました。
モックサーバー
フロントエンドのディレクトリと並列になるようにモックサーバー用のディレクトリを適当に掘る。
$ mkdir backendmock
モックサーバーのプロジェクト作成
以下のコマンドでプロジェクトを作成します。
$ yarn init # type は module。main は、src/index.ts。他は任意で OK
$ yarn add -D @types/node typescript ts-node
$ yarn add -D nodemon # スキーマを変えたら都度読み込み直すためにインストール
tsconfig
は以下のように定義
{
"compilerOptions": {
"target": "esnext",
"lib": ["esnext"],
"module": "esnext",
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
},
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"strict": true,
},
"include": ["src"]
}
GraphQL + Apollo Server のインストール
以下で GraphQL と Apollo Server に必要なモジュールをインストールします。
$ yarn add -D graphql apollo-server @graphql-tools/graphql-file-loader @graphql-tools/load @graphql-tools/schema
バックエンドのメインファイルは以下のようになります。resolvers
や utils/mock
は必要に応じて用意すれば良く、必須ではないです。
import { loadSchemaSync } from '@graphql-tools/load';
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
import { addResolversToSchema } from '@graphql-tools/schema';
import { ApolloServer } from 'apollo-server';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import resolvers from './resolvers';
import {
genRandomDate,
genRandomDateTime,
genRandomFloat,
genRandomInt,
genRandomString,
} from './utils/mock';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const schema = loadSchemaSync(join(__dirname, '../../frontend/schema.graphql'), {
loaders: [new GraphQLFileLoader()],
});
// 自前で型ごとに返す内容をコントロールしたい場合に定義
const mocks = {
Int: genRandomInt,
Float: genRandomFloat,
String: genRandomString,
Date: genRandomDate,
DateTime: genRandomDateTime,
};
const schemaWithResolvers = addResolversToSchema({
schema,
resolvers, // モックでは特に必要ないので `resolvers: { Query: {} }` とかでも OK
});
const server = new ApolloServer({
schema: schemaWithResolvers,
mocks, // mocks: true とした場合は、Apollo が型ごとに適当な値を返す。
});
server.listen().then(({ url }) => {
console.log(`🚀 Mock GraphQL Server is ready at ${url}`);
});
nodemon の設定
nodemon で、ファイルの watch 対象と起動コマンドを定義します。
{
"watch": ["../frontend/schema.graphql", "src"],
"ext": "graphql,ts",
"exec": "node --experimental-specifier-resolution=node --loader ts-node/esm src/index.ts"
}
"scripts": {
"start": "nodemon" // 追加
}
これで、yarn run start
で GraphQL モックサーバーが起動できるようになりました。
開発環境の起動
とりあえず開発環境を起動してみます。
モックサーバーの起動
$ cd vue-graphql-demo/backendmock
$ yarn run start
🚀 Mock GraphQL Server is ready at http://localhost:4000/
こんな感じの画面に辿り着けられれば成功です。Query your server
をクリックすると、Apollo Studio に Sandbox でアクセスできます。
フロントエンドの起動
$ cd vue-graphql-demo/frontend
$ yarn run dev
vite v2.7.6 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 307ms.
こんな感じの画面が見られれば成功です。
今のままだと、まだそれぞれ独立されて立ち上げただけで疎通が確認できていない状態なので、次に疎通するための作業を行います。
GraphQL スキーマの作成
まずは、疎通する際に利用する GraphQL スキーマを試しに作ってみます。
今回は、GraphQL スキーマは、フロントエンド側の持ち物として明確にするために、フロントエンドのディレクトリ配下に置いています。
"""
The query root of GraphQL interface.
"""
type Query {
# Look up a human
human(
# Select the human that matches this name.
name: String!
): Human
}
type Human implements Node {
id: ID!
name: String!
age: Int!
birthDay: Date
hobbies: [String!]!
}
"""
An object with an ID.
"""
interface Node {
# ID of the object.
id: ID!
}
"""
An ISO-8601 encoded date string.
"""
scalar Date
# NOTE:
# `#` のコメントは `"""` に置き換えると Apollo Studio で説明として認識されます。
# Snippet 上おかしくなってしまったので、`#` で書いています。
保存時にブラウザをリロードして Apollo Studio を表示すると、以下のようにクエリエクスプローラーでスキーマ定義に沿ったクエリを書けるようになります。
またフロントエンドで型定義を利用するために、先ほど環境構築時に用意した以下のコマンドを実行します。
$ yarn run gql:gen
成功すると、codegen.yml
に定義したとおり、src/generated/graphql.ts
に Typescript のコードが生成されているはずです。
クエリの実装
作成したスキーマに沿って、GraphQL クエリを App.vue
に以下のように実装してみます。
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable';
import gql from 'graphql-tag';
import HelloWorld from './components/HelloWorld.vue';
const { result, loading } = useQuery(gql`
query getHuman($name: String!) {
human(name: $name) {
id
name
age
birthDay
hobbies
}
}
`,
{
name: 'Me'
})
</script>
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
<div v-if="loading">
GraphQL loading
</div>
<div v-else>
{{ JSON.stringify(result) }}
</div>
</template>
保存した後に、画面を確認してみると、クエリの実行結果が画面に反映されていることが確認できました。(すごい雑だけど)
以上で、フロントエンドとモックサーバー間で GraphQL での疎通が確認できました
まとめ
本記事では、Vite / Vue / Apollo / GraphQL Code Generator を使って GraphQL モックサーバーを作り方を説明しました。上にも書いてますが、vue-graphql-demo に最終的な結果を置いています。
記事が長くなるので (十分長い)、細かなところは割愛しましたが、やってみて割とフロントエンドの開発は快適にできるかなという印象を受けました。
本記事では触れていませんが、GraphQL を使うにあたって、リゾルバーの実装などがハードルが高い & 肝になってくるところだと思うので、その辺りを引き続き勉強していければと思っています。
今回の経験が、一部ではありますが、GraphQL 導入時の一助/参考になれば幸いです。
Happy Coding!