前提
OS:macOS Monterey 12.4
Node.js:v18.12.0
ApolloもGraphQLも今回初めて使いました。
GraphQLとは
公式
以下の記事がわかりやすかったです。
Apolloとは
ApolloはGraphQLを扱うためのライブラリを提供しています。例えばGraphQLサーバーの実装をしたい場合は「apollo-server」というライブラリが、iOSアプリでGraphQLサーバーとのやりとりを実装したい場合は「apollo-ios」というライブラリが提供されています。
公式チュートリアル実施
公式のチュートリアル通りに進めたら詰まることなくできました。一応ざっくり手順を残しておきます。最低限の説明やコードだけ記載するので、詳細は公式のチュートリアルをご覧ください。
www.apollographql.com
プロジェクト作成
任意の場所に今回用のディレクトリを作成・移動し、プロジェクトのセットアップを行います。
mkdir graphql-server-example cd graphql-server-example npm init --yes
ライブラリのインストール
GraphQLとApollo Serverのライブラリをインストールします。
npm install @apollo/server graphql
実装準備
index.jsを用意します。
touch index.js
package.jsonを更新します。
{ "type": "module", "scripts": { "start": "node index.js" } }
スキーマの定義
import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; const typeDefs = ` type Book { title: String author: String } type Query { books: [Book] } `;
レスポンス用データの定義
const books = [ { title: 'The Awakening', author: 'Kate Chopin', }, { title: 'City of Glass', author: 'Paul Auster', }, ];
リゾルバーの定義
const resolvers = { Query: { books: () => books, }, };
GraphQLサーバー起動
const server = new ApolloServer({ typeDefs, resolvers, }); const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, }); console.log(`🚀 Server ready at: ${url}`);
下記コマンドを実行すればGraphQLサーバーが起動します。
npm start
Sandboxを使ってクエリを試す
GraphQLサーバーが起動していれば http://localhost:4000 にアクセスすることでApollo Sandboxを使用できます。 「ExampleQuery」を押下すると「query ExampleQuery〜」と書かれている内容でリクエストすることができます。
クエリは自由に弄って試せます。クエリを新しく書いて、本のタイトルだけ取得してみます。
クエリは名前なしでも定義し実行できます。「query」すら省略可能です。
公式チュートリアル実施後の状態から色々試す
引数の使用
クエリ実行時に引数を使用してみます。本の「title」を渡して一致する本の「auther」を返してもらうイメージで作ってみます。下記のようにスキーマのQueryとリゾルバーを修正します。
const typeDefs = ` type Book { title: String author: String } type Query { books(title: String): [Book] } `; const resolvers = { Query: { books: (parent, args, context, info) => books.filter((book) => book.title === args.title), }, };
リゾルバーの引数についてはドキュメントをご覧ください。
www.apollographql.com
以下のように、本の「title」を渡して一致する本の「auther」を返してもらうことができました。
引数に変数を使用
これはApollo Serverの話ではなくGraphQLのリクエスト側の話ですが、以下のように変数を使用することができます。コードを弄る必要はありません。
デフォルト引数も使えます。
フラグメント
リクエストするフィールドの定義をフラグメントという形でひとまとめにして共通化することができます。コードを弄る必要はありません。
インラインフラグメントというのもあるのですが、ちょっとしっくりきてないのでまた別の機会に…
null非許容
型の後ろに「!」をつけるとnullが入らなくなります。例えば下記のように引数の型に「!」をつけるとnullを受け付けなくなります。
const typeDefs = ` type Book { title: String author: String } type Query { books(title: String!): [Book] } `;
当然ですがドキュメントにも記載があります。
Queryを複数定義する
同じブロック内に複数書いても別で書いても問題なく動きました。
const typeDefs = ` type Book { title: String author: String } type Query { books(title: String!): [Book], books2: [Book], books3: [Book] } type Query { books4: [Book] } `; const resolvers = { Query: { books: (parent, args, context, info) => books.filter((book) => book.title === args.title), books2: () => books, books3: () => books, }, Query: { books4: () => books, }, };
Mutationを定義する
ここまでクエリのオペレーションタイプは全てQueryでしたので、Mutationも試してみます。とはいってもQueryと定義方法は変わりません。
const typeDefs = ` type Book { title: String author: String } type Query { books(title: String!): [Book], } type Mutation { mutation_test_books: [Book] } `; const resolvers = { Query: { books: (parent, args, context, info) => books.filter((book) => book.title === args.title), }, Mutation: { mutation_test_books: () => books, }, };
Subscriptionを試すのは別の機会に…
スキーマを外部ファイルに定義する
下記の記事が参考になりました。
zenn.dev
graphqlファイルにスキーマを定義し、読み込んで使用します。まずは必要なライブラリをインストールします。
npm install @graphql-tools/graphql-file-loader @graphql-tools/load @graphql-tools/schema
graphqlファイルを作り、スキーマを定義します。
schema.graphqlから読み込むように書き換えます。チュートリアルとの違いは、ライブラリを使ったschema.graphqlの読み込みと設定になります。
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'; import { loadSchemaSync } from '@graphql-tools/load'; import { addResolversToSchema } from '@graphql-tools/schema'; // 読み込み const schema = loadSchemaSync('./schema.graphql', { loaders: [new GraphQLFileLoader()], }); // 設定 const schemaWithResolvers = addResolversToSchema({ schema, resolvers }); const server = new ApolloServer({ schema: schemaWithResolvers });
全体としては以下のような感じになります。
type Book { title: String author: String } type Query { books(title: String): [Book] } type Mutation { mutation_test_books: [Book] }
import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'; import { loadSchemaSync } from '@graphql-tools/load'; import { addResolversToSchema } from '@graphql-tools/schema'; const schema = loadSchemaSync('./schema.graphql', { loaders: [new GraphQLFileLoader()], }); const books = [ { title: 'The Awakening', author: 'Kate Chopin', }, { title: 'City of Glass', author: 'Paul Auster', }, ]; const resolvers = { Query: { books: (parent, args, context, info) => books.filter((book) => book.title === args.title), }, Mutation: { mutation_test_books: () => books, }, }; const schemaWithResolvers = addResolversToSchema({ schema, resolvers }); const server = new ApolloServer({ schema: schemaWithResolvers }); const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, }); console.log(`🚀 Server ready at: ${url}`);