【GraphQL入門】Apollo Serverの設定とVueクライアントからのデータ取得
GraphQLをクライアント側で使用した経験またはGraphQLという単語を耳にしたことはあるが実際にGraphQLのサーバ側ではどのような設定が行われているか知らない人も多いかと思います。
本文書ではGraphQLサーバ側ではどのような設定が必要なのかまたクライアントはどのようなコードでGraphQLサーバにアクセスしなければならないかをシンプルなデータを利用して説明を行なっています。シンプルなデータを利用することでGraphQLサーバの基本の基本を完全に理解することができます。また後半ではJavaScript, Vue.js上からfetch関数でGraphQLサーバからのデータ取得の方法も確認を行いVue 3にApollo Clientをインストールしてデータの取得を行なっています。一通り読むことでGraphQLのサーバ設定、クライアントからのデータ取得までの一連の流れを理解することができます。
動作確認にはGraphQLサーバが構築できるオープンソースのApollo Serverを利用しています。Apollo ServerはJavaScriptのライブラリなのでJavaScriptの知識が必要となります。また環境を構築する際はNode.jsをインストールしておく必要があります。
目次
GraphQLとは
GraphQLにQL(Query Language)とという名前がついているようにデータベースから取得する際に利用するSQLのように取得したいデータをクライアント側でクエリーで設定して送信してデータの取得を行います。これまでのみなさんが使い慣れたREST APIのようにあるデータが欲しい場合に決められたエンドポイントに対してアクセスを行うといった形(ユーザ一覧の情報が欲しい場合は/usersにアクセス)ではありません。またエンドポイントに対してGET, POST, PUT, DELETEといったメソッドによって実行したい処理を指定することはありません。送信するエンドポイントは一つだけ存在し、その一つのエンドポイントに対して送信するクエリーを変更することで処理する内容や取得したいデータを変更します。クエリーを使ってデータを取得する場合は欲しい項目(ユーザデータなら名前)のみ指定することができるので利用予定のないデータを取得する必要がなくなります。スキーマを使ってデータの定義を行うため扱うデータが明確化されます。
GraphQLのポイントとして以下のような点が挙げられます。
- エンドポイントは1つ
- クライアントサイドでクエリーを設定
- 欲しいデータを指定
- スキーマによりデータ定義
Apolloサーバ用プロジェクトの作成
Apollo Server用のプロジェクトの作成を行います。任意の名前のディレクトリを作成し、ディレクトリの作成後、作成したディレクトリに移動してください。
% mkdir graphql-server-example
% cd graphql-server-example
package.jsonファイルを作成するためにnpm init -yコマンドを実行します。実行するとディレクトリ内にpackage.jsonファイルが作成されます。
% npm init -y
% ls
package.json
Apollo Serverには2つのライブラリが必要となります。
- apollo-server
- graphql
npmコマンドを使ってインストールを行います。
% npm install apollo-server graphql
インストールが完了したら、index.jsファイルを作成してください。index.jsファイルにApollo Serverに必要なコードを記述していきます。
% touch index.js
GraphQLの設定
index.jsファイルにGraphQLサーバの設定を記述していきます。index.jsファイルを開いて、apollo-serverからapollo serverに必要なモジュールをrequireします。
const { ApolloServer, gql } = require('apollo-server');
スキーマの定義
GraphQLはGraphQLのスキーマを記述するためだけに利用させる独自のスキーマ定義言語(Schema Definition Language)を持っています。
通常はスキーマを定義して定義に合わせてデータを作成することになりますがスキーマを元に作成されたデータがどのように形か確認するためにサンプルデータをindex.jsファイルに記述します。booksはtitle, authorを持つデータによって構成されています。
const { ApolloServer, gql } = require('apollo-server');
const books = [
{
title: 'Harry Potter and the Chamber of Secrets',
author: 'J.K. Rowling',
},
{
title: 'Jurassic Park',
author: 'Michael Crichton',
},
];
データはtitle, authorで構成されていることは確認済みなのでSDLを利用して定義します。typeを使って名前をつけ以下のように記述を行うことができます。typeの名前は慣例でキャメルケースを利用します。type Bookはtitleとauthorのフィールドで構成されておりそれぞれには文字列が入っているのでデータの型はStringとなります。Stringの後の!マークはそのフィールドが必須であることを表します。
type Book {
title: String!
author: String
}
スキーマについてはindex.jsにそのまま記述するのではなくgqlの中に記述します。typeDefsはType Definitionsの略です。
const typeDefs = gql`
type Book {
title: String
author: String
}
`
GraphQLではクエリーを利用してデータを取得することができます。データ取得する際に利用するQueryタイプのフィールドを設定します。QueryタイプはBookタイプとは異なりクライアントがGraphQLを利用してアクセスする際に利用します。queryでbooksを指定して実行するとBookタイプの配列がクライアントに戻されることを定義しています。何もデータがない時は空の配列が戻されます。Queryタイプは必ず設定を行う必要があり、Queryタイプの中にのフィールドにクライアントが利用するqueryを追加していくことになります。
const typeDefs = gql`
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
Resolverの設定
サンプルデータとスキーマの設定は行いましたが、アクセスがあった場合はどのようなデータを戻すかが設定されていません。その設定を行うのがResolverです。
下記のコードではresolversのQueryの中ではbooksと指定していますが、これがスキーマのQueryタイプの中で定義したbooksです。クライアントからのqueryでbooksが指定された場合はbooksプロパティの値である関数が実行されます。関数を使うことでどのようなデータをクライアントに戻すのか制御することができます。
const resolvers = {
Query: {
books: () => books,
},
};
Apollo Serverの設定
サンプルデータ、スキーマ、Resolverの設定が完了したので、Apolloサーバの設定を行います。
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
設定が完了したのでサーバの起動を行います。ポート4000で起動することがわかります。
% node index.js
🚀 Server ready at http://localhost:4000/
Apollo Studioの利用
ブラウザで http://localhost:4000/にアクセスすると下記の画面が表示されます。”Query your server”をクリックします。
クリックするとApollo Studioツールでこの画面からGraphQLを実行しAppoloサーバからデータを取得することができます。
Queryの実行
画面中央のOperationにデフォルトでExampleQueryが記述されているので”ExampleQuery”ぼたんをクリックすることでQueryを実行することができます。ボタンをクリックすると右側にはResponseが表示されデータのtitleのみ表示されます。表示されているtitleはindex.jsに追加したbooksの配列のデータです。ExampleQueryではtitleのみを設定しているためtitleのみ表示されています。
ExampleQueryの中にauthorを追加することでtitle, authorを含むデータを取得することができるようになります。
Apollo Serverを使うことによって簡単にGraphQLを実行する環境を作成することができました。
booksに保存されているデータを取得することができましたがあるauthorのtitleのみ取得したい場合にどのような設定を行えばいいか確認を行います。スキーマとResorversに追加をおこないます。
スキーマではtype Queryの中にbookを追加し引数にauthorを取り必須とします。結果はBookが戻されるようにしています。
const typeDefs = gql`
type Book {
title: String
author: String
}
type Query {
books: [Book]
book(author: String!): Book
}
`;
resolversに実行する処理を追加します。
const resolvers = {
Query: {
books: () => books,
book: (parent, args) => {
let book = books.find((book) => book.author === args.author);
return book;
},
},
};
設定は完了です。
Apollo Studioでtitleが取得できるのか確認を行います。bookの引数であるauthorをVariableで設定を行います。Operationsの中のqueryも先ほどとは異なり引数の$authorが設定されています。実行すると変数に一致するauthoerのtitleが表示されます。
しかしここで一つの疑問が浮かびます。クライアントからアクセスする際にはApollo Studioを利用することはできません。どのようにクライアントがGraphQLのデータにアクセスするのでしょう。その方法について確認していきます。
fetch関数によるGraphQLサーバからデータ取得
クライアントからGraphQLへのアクセスにはGraphQL ClientがありますがGraphQL Clientを利用しなくてもアクセスすることは可能です。ここではJavaScriptのfetch関数を利用します。
fetchを利用してGraphQLにアクセスを行いデータが取得できるか確認を行います。
fetchを使ってデータを取得するためにhtmlファイルを作成し、scriptタグにfetchを利用したコードを記述します。
<!DOCTYPE html>
<html lang="ja"">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<h1>Fetchを利用したGraphQLへのアクセス</h1>
</body>
<script>
async function fetchBooks() {
const response = await fetch("http://localhost:4000", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `query{
books{
title
author
}
}`,
}),
});
const data = await response.json();
console.log(data);
}
fetchBooks();
</script>
</html>
fetchを利用してGraphQLにアクセスする場合のポイントがいくつかあります。
- メソッドにはPOSTを利用
- bodyにGraphQLのqueryを記述しJSONに変換
- headersにはContent-Typeのapplication/jsonを指定
console.logの内容を確認するとサンプルデータの内容が表示されていることが確認できます。
GraphQLのClientを利用しなくてもGraphQLサーバからfetchを利用してデータが取得できることが確認できました。
Vueの設定
Viteによるプロジェクトの作成
Viteを利用してVueのプロジェクトの作成を行います。Vueのプロジェクトを作成する余裕がない人向けにcdnを使った場合の処理も記述しています。
% npm init vite@latest vue-apollo -- --template vue
vue-apolloフォルダが作成されるのでvue-apolloフォルダに移動してnpm installを実行します。
% cd vue-apollo
% npm install
npm run devコマンドで開発サーバを起動します。
fetch関数によるデータに取得
JavaScriptからデータの取得ができたのでVue上でのfetch関数を利用してデータを取得することができます。Composition APIのscriptタグを利用してコードを記述しています。
<script setup>
import { ref } from 'vue';
const books = ref([]);
const fetchBooks = async () => {
const response = await fetch('http://localhost:4000', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `query{
books{
title
author
}
}`,
}),
});
const { data } = await response.json();
books.value = data.books;
};
fetchBooks();
</script>
<template>
<h1>Fetchを利用したGraphQLへのアクセス</h1>
<ul>
<li v-for="(book, index) in books" :key="index">
{{ book.title }}/{{ book.author }}
</li>
</ul>
</template>
cdnによるVue.jsでのデータの取得
Vueプロジェクトではなくcdnを利用してfetch関数によりデータの取得を行うこともできます。
fetch関数の内容は変わっていませんが、取得したデータをデータプロパティのbooksに保存して、v-forで展開を行っています。
<!DOCTYPE html>
<html lang="ja"">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<h1>Fetchを利用したGraphQLへのアクセス</h1>
<div id="app">
<ul>
<li v-for="(book,index) in books" :key="book">{{ book.title }}/{{ book.author }}</li>
</ul>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
new Vue({
el: "#app",
data:{
books:[]
},
methods:{
async fetchBooks() {
const response = await fetch("http://localhost:4000", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `query{
books{
title
author
}
}`,
}),
});
const { data } = await response.json()
return data;
}
},
mounted(){
this.fetchBooks().then(({books}) => this.books = books);
}
})
</script>
</html>
ブラウザでidex.htmlファイルを確認するとGraphQLに保存したサンプルデータが表示されることを確認できます。
Apollo Clientによるデータ取得
fetch関数を利用することでApollo Severからデータの取得が行えることがわかりました。Apollo ClientをVueプロジェクトにインストールを行いApollo Clientからのデータの取得方法を確認します。
ライブラリのインストール
Vue 3でApollo Clientを利用するためのライブラリはドキュメントhttps://v4.apollo.vuejs.org/を参考に行なっていきます。
% npm install --save graphql graphql-tag @apollo/client
その後Composition (Advanced) APIを確認して@vue/apollo-composableをインストールします。
% npm install --save @vue/apollo-composable
main.jsファイルに更新する内容についてもドキュメントを参考に行なっていきます。
import { createApp, provide, h } from 'vue';
import { DefaultApolloClient } from '@vue/apollo-composable';
import App from './App.vue';
import {
ApolloClient,
createHttpLink,
InMemoryCache,
} from '@apollo/client/core';
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');
main.jsでのApollo Clientの設定が完了したらコンポーネントで利用できるようになります。
App.vueファイルでuseQueryを利用してデータの取得を行います。
<script setup>
import { useQuery } from '@vue/apollo-composable';
import gql from 'graphql-tag';
const { result } = useQuery(gql`
query {
books {
title
author
}
}
`);
</script>
<template>
<h1>Fetchを利用したGraphQLへのアクセス</h1>
<ul v-if="result && result.books">
<li> v-for="(book, index) in result.books" :key="index">
{{ book.title }}/{{ book.author }}
</li>
</ul>
</template>
まとめ
JavaScriptのfetch関数を利用してApollo Serverからデータを取得することができることがわかりました。Vue.jsではApollo Clientのライブラリを利用することでfetch関数よりも短いコードでデータの取得が行えることも確認できました。