最近Next.jsを使って開発することが多いです。ちょっとした物を作る時はFirebaseと組み合わせることが多いのですが、ガッツリAPIを作り込みたい時はGraphQLで書くこともあります。
GraphQLを使う時はGraphQL SDLでスキーマを定義することになると思います。特に、GraphQLを書く場合はスキーマを決めてからサーバーやクライアントの実装に着手することが多いのではないでしょうか。スキーマを書くことでコード生成を行うことができたり、型チェックによる恩恵を受けることができます。
スキーマがあるなら、クライアント側のアプリケーションでは、サーバーサイドのアプリケーション開発を待たずにモックサーバーを作って開発を進めることができるはずです。今回はNext.jsとGraphQLを組み合わせて、スキーマからモックサーバーを作り、開発中はそこのモックサーバーにアクセスすることで
- サーバーサイドの開発完了を待たずにNext.jsアプリを開発できる
- インターネット接続がない状態でもアプリ開発を続けることを可能にする
- モックサーバーで任意のデータを返すことをできるようにし、サーバーサイドに依存しない表示の確認を可能にする
ことを目的にします。
サンプルアプリ
サンプルアプリを作ったので、こちらのpackage.jsonあたりをみていただければ何をしているかわかると思います。
以下では何をどのように行ったかを解説します。
やったこと
プロジェクト作成
まずは、create-next-appでプロジェクトを作ります。
npx create-next-app --example with-apollo graphql-next-app
この状態で実行すると、すでにGraphQLが使えるNext.jsアプリができています。
cd graphql-next-app yarn dev open http://localhost:3000
ただ、実際のGraphQLサーバーに依存している状態なので、インターネット接続のない環境などではアクセスすることができません。試しにWi-Fiを切断した状態でリクエストを送ると以下のような表示になります。
これを解決していきましょう。
スキーマを準備する
モックサーバーを作成するためにスキーマを使います。このアプリで使っているGraphQLサーバーは以下のURLからアクセス可能で、スキーマもこちらからダウンロード可能です。
右上にあるボタンからSDL形式でダウンロードしましょう。
ここでダウンロードしたファイルは、./schema.graphql
という名前でプロジェクト直下に配置しておきます。
Apollo Serverを使ってモックサーバーを作る
先ほどダウンロードしたスキーマを使ってモックサーバーを作りましょう。これはApollo Serverを使うと簡単に行えます。単純なモックサーバーは以下の10行のコードで実現可能です。
// ./mock/index.js const { ApolloServer } = require("apollo-server"); const { makeExecutableSchema } = require("graphql-tools"); const { importSchema } = require("graphql-import"); const typeDefs = importSchema("../schema.graphql"); const schema = makeExecutableSchema({ typeDefs }); const server = new ApolloServer({ schema, mocks: {} }); server.listen().then(({ url }) => { console.log(`🚀 Server ready at ${url}`); });
ただ、今回使っているGraphQLのスキーマでは、DateTimeを扱うために独自のスカラ値を定義しています。Apollo Serverはこのスカラ値をモックとしてどう扱えば良いかわかりません。今回のGraphQLスキーマでモックサーバーを作るためには、DateTimeの情報を追加する必要があります。独自のスカラ値の定義は makeExecutableSchema()
メソッドで渡します。
DateTimeの場合は以下のようなリゾルバを生成すると良いでしょう。
const resolvers = { DateTime: new GraphQLScalarType({ name: "DateTime", description: "DateTime", serialize(data) { return data.toISOString(); }, parseValue(data) { return new Date(data); }, parseLiteral(ast) { return new Date(ast.value); } }) };
また、モックとして返す値も追加しましょう。
const server = new ApolloServer({ schema, mocks: { DateTime: () => { return new Date(); // It returns current Date for all response. } } });
ファイル全体はこのようになりました。
// https://github.com/kouki-dan/GraphQL-schema-first-next-app/blob/3f4ac80123e461a62797bbc79136b59178c52545/mock/index.js const { GraphQLScalarType } = require("graphql"); const { ApolloServer } = require("apollo-server"); const { makeExecutableSchema } = require("graphql-tools"); const { importSchema } = require("graphql-import"); const typeDefs = importSchema("../schema.graphql"); const resolvers = { DateTime: new GraphQLScalarType({ name: "DateTime", description: "DateTime", serialize(data) { return data.toISOString(); }, parseValue(data) { return new Date(data); }, parseLiteral(ast) { return new Date(ast.value); } }) }; const schema = makeExecutableSchema({ typeDefs, resolvers }); const server = new ApolloServer({ schema, mocks: { DateTime: () => { return new Date(); } } }); server.listen().then(({ url }) => { console.log(`🚀 Server ready at ${url}`); });
これを ./mock/index.js
に配置し、実行するための設定を ./mock/package.json
として以下のようにします。
{ "name": "mock", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "apollo-server": "^2.9.13", "graphql": "^14.5.8", "graphql-import": "^0.7.1" } }
以下のコマンドで実際にモックサーバーが動作するかを確認しましょう。
cd mock npm install npm start
http://localhost:4000 を開くとPlaygroudが起動します。クエリを書くとモックデータが返ってくることが確認できます。
これでモックサーバーが完成しました。実際に使う時はモックやリゾルバをカスタマイズして使うことになると思います。
Next.jsアプリとモックサーバーを接続する
GraphQLに接続するNext.jsアプリと、任意のデータを返すGraphQLサーバーが実現できたので、これを開発時に組み合わせることで最初に述べた3つの目的を満たすことができます。
npm-run-all
を使うとこの2つのサーバーを簡単に同時に起動することができます。以下のコマンドで追加しましょう。
yarn add -D npm-run-all
プロジェクト直下のpackage.jsonを以下のように書き換えます。
"scripts": { "mock": "npm start --prefix mock", "dev-mock": "GRAPHQL_ENDPOINT=\"http://localhost:4000/\" run-p dev mock", "dev": "next", "build": "next build", "start": "next start", "postinstall": "npm install --prefix mock" },
mock
, dev-mock
, postinstall
が追加されました。
mock
は、先ほど作ったモックサーバーを起動します。
dev-mock
は、npm-run-all
を使ってNext.jsの開発サーバーと、GraphQLのモックサーバー(mock
)を同時に起動します。後ほど説明しますが、GraphQLのエンドポイントを書き換えるために環境変数でモックサーバーのURLを渡しています。
postinstall
では、npm install
時に ./mock 以下でもインストールを行うように設定しています。
dev-mock
の説明で書いた通り、開発用のモックサーバーと、実際のサーバーを切り替えることができるようにしなければなりません。next.config.js
を編集して実現します。
module.exports = { publicRuntimeConfig: { graphqlEndpoint: process.env.GRAPHQL_ENDPOINT || "https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn", } };
lib/apollo.js
を編集し、この値を利用できるようにします。
function createApolloClient(initialState = {}) { // Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient const { publicRuntimeConfig } = getConfig(); return new ApolloClient({ ssrMode: typeof window === 'undefined', // Disables forceFetch on the server (so queries are only run once) link: new HttpLink({ uri: publicRuntimeConfig.graphqlEndpoint, // Server URL (must be absolute) credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers` fetch, }), cache: new InMemoryCache().restore(initialState), }) }
これで完成です!全体のdiffはこちらで見れます。
早速モックサーバーモードで起動してみましょう。先ほどpackage.jsonに定義した dev-mock
スクリプトを使います。
npm run dev-mock
http://localhost:3000 を開くとモックサーバーに繋がってることを確認できます!
値をカスタマイズする
ここまでで目的は達成されました、最後に値をカスタマイズできるようにしましょう。./mock/index.js
を編集し、mocks
に返したい値を与えることで実現できます。以下はUserの名前を僕の名前(Kouki Saito)に変更する例です。
const server = new ApolloServer({ schema, mocks: { DateTime: () => { return new Date(); }, User: () => { return { firstName: "Kouki", lastName: "Saito", } }, } });
Playgroundを見ると、このように変更できていることがわかります。
まとめ
GraphQLは、Webアプリケーションを開発するための強力なツールです。 ApolloはGraphQLを使用するための優れたプラットフォームです。Apollo Serverでモックを作ることで、クライアントアプリを効率的に開発することにも役立ちます。 この記事で説明した内容は以下のサンプルリポジトリで確認できます。お役に立てば幸いです。