M3 ではグローバル CTO の Brian が、サービスの海外展開や技術基盤の共通化などを積極的に進めています。その中のプロジェクトの1つとして、アメリカで提供している医療ニュースのリニューアルにチャレンジしています。2018 年 5 月には日本オフィス所属のイギリス人エンジニア @christophrowley と日本人のエンジニア (筆者)が 1 ヶ月ほどニューヨークに出張してリニューアルの検討をしてきました。
( ↑ Chrisが撮影してくれた NY の写真 )
今回の記事は、リニューアルで採用を検討している GraphQL を Apollo + JavaScript で作るチュートリアルです。
TL;DR
GraphQL の概要
GraphQL は Facebook が発表した、クライアントアプリがサーバから柔軟にデータを取得できるように設計されたクエリ言語です。
GraphQL の概要を知るのに How to GraphQL (英語)がオススメです。GraphQL の初心者向けの説明資料や 40 分ほどの動画を掲載されています。React.js や Ruby での実装チュートリアルなどもあります。GraphQL の概要を知るのに最適なサイトです。
GraphQL にトライ
GraphQL API Explorer では GitHub が公開している GraphQL の API を簡単に試すことができます。GraphQL がいったいどういうものかを触ってみるのにちょうど良いサンプルです。 右上の「Docs ボタン」をクリックすると GraphQL のドキュメントを見ることができるので、参考にしつつクエリを書いてみてください。
GraphQL のメリット
一般的な GraphQLのメリット
GraphQL を採用するメリットは次のようなものがあります。
- 複数の API リクエストを一つにまとめることができ、リソースを無駄なく取得できる
- Web/iOS/Android の共通したバックエンドを効率的に開発ができる
- スキーマファースト、ドキュメントファーストで開発ができる
- 既存・新規アプリや外部サービスの API を束ねる API Gateway として活用できる
M3 US におけるメリット
M3 US で GraphQL を採用したいと考えている理由は:
- USの開発チームは人数が少ないため、Contentful や Okta のような外部サービスを利用したい。 GraphQLはそういった外部サービス、既存・新サービスのAPI Gateway として活用できそう
- USは利用者のスマートフォン比率が高いので、バックエンドをGraphQLにしてスマホアプリの開発を加速させたい (バックエンドとフロントエンドが非同期に開発しやすかったり、仕様と一致したドキュメントを整備しやすいなど)
Apollo プロジェクトについて
Apollo GraphQL は Meteor.js を開発しているチームが中心となって 開発している GraphQL のための OSS、サービスです。
代表的なツールとしては、
チュートリアル
今回は Node.js による GraphQL バックエンドと、GraphQL を使ってデータを表示する React フロントエンドのチュートリアルを紹介します。動作検証はnode.js v8.11.2
で行っています。
GraphQL バックエンド
プロジェクトの初期状態の構築
Express.js を使って、GraphQL のバックエンドを構築します。ターミナルで次のコマンドを実行してください。
# プロジェクトのフォルダを作成して移動 mkdir express-graphql && cd express-graphql # Nodeプロジェクトの初期状態を構築 npm init # ライブラリをインストール npm install --save express body-parser cors graphql apollo-server-express graphql-tools graphql-tag
ちなみにインストールしたライブラリは次の通りです。
- express : Web アプリ用のミニマムなフレームワーク
- body-parser : POST パラメータを JSON で取得するのに利用
- cors : Cross-origin resource sharing (CORS)を許可するためのミドルウェア
- graphql : GraphQL のリファレンス実装で、graphql を使うときに利用
- apollo-server-express : Apollo Server を Express.js につなげて使うための実装
- graphql-tools : GraphQL のスキーマなどを定義するための実装
そしてpackage.json
のscript
を次のように変更します。
"scripts": { "start": "node ./index.js" }
GraphQL サーバアプリの作成
次にindex.js
を作成して次のように記述します。
const express = require("express"); const bodyParser = require("body-parser"); const cors = require("cors"); const { graphqlExpress, graphiqlExpress } = require("apollo-server-express"); const { makeExecutableSchema } = require("graphql-tools"); // モックデータ const books = [ { title: "Harry Potter and the Sorcerer's stone", author: "J.K. Rowling", price: 2000 }, { title: "Jurassic Park", author: "Michael Crichton", price: 3000 } ]; // GraphQLのスキーマ情報 const typeDefs = ` type Query { books: [Book] } type Book { title: String, author: String, price: Int } `; // resolver(データ処理)の設定 // DBからデータを取得したり、APIを呼び出したりする処理もここで記述 const resolvers = { Query: { books: () => books } }; // GraphQL の Schema 設定 const schema = makeExecutableSchema({ typeDefs, resolvers }); // Expressの初期化 const app = express(); // Cross-origin resource sharing (CORS) の設定 const corsOptions = { origin: "http://localhost:3000", optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 }; // GraphQLのエンドポイントの追加 app.use( "/graphql", bodyParser.json(), cors(corsOptions), graphqlExpress({ schema }) ); // GraphiQLのエンドポイントの追加 (テストで使う GraphQLのWeb GUI) app.use("/graphiql", graphiqlExpress({ endpointURL: "/graphql" })); // サーバの起動 app.listen(4000, () => { console.log("Go to http://localhost:4000/graphiql to run queries!"); }); module.exports = app;
GraphQL サーバアプリの動作確認
先程作成した Express.js のアプリを起動するために、ターミナルで次のコマンドを実行してください。
npm run start
http://localhost:4000/graphiql
にアクセスできれば成功です。ちなみに、これは GraphQL のクエリを簡単に試すことができる Web 上のエディタです。例えば、
{ books { title author } }
を左側のペインに入力して再生ボタン(Execute Query)を押すと GraphQL からデータを結果を取得できます。
React フロントエンド
React アプリの初期構築
create-react-app を使って、React フロントエンドのアプリを構築します。ターミナルで次のコマンドを実行します。
# create-react-app のインストール (インストール済の場合はスキップ) npm install -g create-react-app # create-react-app を使って、プロジェクトを作成してそのフォルダに移動 create-react-app react-graphql && cd react-graphql # ライブラリのインストール npm install # 動作確認 npm run start
http://localhost:3000
にアクセスして以下の画面が表示されたら成功。
Apollo Client の導入
Apollo Client で使うライブラリをインストールして、GraphQL にアクセスできるようにします。
npm install apollo-boost graphql-tag graphql react-apollo --save
- apollo-boost : Apollo Client を設定なしで使えるようにしたライブラリセット
- graphql-tag : GraphQL クエリ記述をサポート
- graphql : GraphQL のリファレンス実装で、graphql を使うときに利用
- react-apollo : React.js で Apollo を簡単に使えるようにするライブラリ
GraphQL サーバからデータを取得
次に、./src/App.js
を次のように変更します。
import React, { Component } from "react"; import ApolloClient from "apollo-boost"; import { ApolloProvider } from "react-apollo"; import Books from "./Books"; const client = new ApolloClient({ uri: "http://localhost:4000/graphql" }); class App extends Component { render() { return ( <ApolloProvider client={client}> {/* ↑ Apolloクライアント(GraphQLのクエリ)を使えるように設定 */} <div> <h2>My first Apollo app</h2> <Books /> </div> </ApolloProvider> ); } } export default App;
次に、./src/Books.js
を作成して、GraphQL から書籍book
の情報を取得します。
import React from "react"; import { Query } from "react-apollo"; import gql from "graphql-tag"; const Books = () => ( <Query query={ /* GraphQLのクエリ */ gql` { books { title author price } } ` } > {/* GraphQLのクエリの実行結果の処理、成功したら結果を表示 */} {({ loading, error, data }) => { if (loading) return <p>Loading...</p>; if (error) return <p>Error</p>; return data.books.map(course => ( <div key={course.title}> <p>title: {`${course.title}`}</p> <p>author: {`${course.author}`}</p> <p>price: {`${course.price}`}</p> <hr /> </div> )); }} </Query> ); export default Books;
React アプリの動作確認
作成した React フロントエンドを起動するために、ターミナルで次のコマンドを実行してください。
# React フロントエンド(react-graphql)フォルダで以下のコマンドを実行 npm run start
ブラウザで、http://localhost:3000/
にアクセスして、GraphQL サーバのデータを取得できていれば成功です。
Apollo Engine
GraphQL のパフォーマンスの監視などを行うサービスとして Apollo Engine があります。このサービスは、GraphQL のクエリの実行日時、処理時間、処理時間の内訳、エラーの発生率やキャッシュヒット率などを確認できます。100 万クエリ / 月までは無料で利用することができます。
まずは Apollo Engine の Web 画面からAPI KEY
を取得します。
次に apollo-engine と環境変数を管理する dotenv をインストールします。
# Apollo Server(Express.js)のフォルダへ移動 cd express-graphql # apollo-engine dotenv をインストール npm install --save apollo-engine dotenv # .env に先程取得したAPI KEYを設定 echo "APOLLO_ENGINE_API_KEY=XXX" > .env
Apollo Server(Express.js)のプロジェクトのindex.js
を次のように変更。
require("dotenv/config"); // .envの環境変数の読み込み const express = require("express"); const bodyParser = require("body-parser"); const cors = require("cors"); const { ApolloEngine } = require("apollo-engine"); const { graphqlExpress, graphiqlExpress } = require("apollo-server-express"); const { makeExecutableSchema } = require("graphql-tools"); const { APOLLO_ENGINE_API_KEY } = process.env; // モックデータ const books = [ { title: "Harry Potter and the Sorcerer's stone", author: "J.K. Rowling", price: 2000 }, { title: "Jurassic Park", author: "Michael Crichton", price: 3000 } ]; // GraphQLのスキーマ情報 const typeDefs = ` type Query { books: [Book] } type Book { title: String, author: String, price: Int } `; // resolver(データ処理)の設定 // DBからデータを取得したり、APIを呼び出したりする処理もここで記述 const resolvers = { Query: { books: () => books } }; // GraphQL の Schema 設定 const schema = makeExecutableSchema({ typeDefs, resolvers }); // Expressの初期化 const app = express(); // Cross-origin resource sharing (CORS) の設定 const corsOptions = { origin: "http://localhost:3000", optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 }; // GraphQLのエンドポイントの追加 app.use( "/graphql", bodyParser.json(), cors(corsOptions), graphqlExpress({ schema }) ); // GraphiQLのエンドポイントの追加 (テストで使う GraphQLのWeb GUI) app.use("/graphiql", graphiqlExpress({ endpointURL: "/graphql" })); // Apollo Engineのインスタンスの作成 const engine = new ApolloEngine({ apiKey: APOLLO_ENGINE_API_KEY, // メモリキャッシュの設定 stores: [ { name: "inMemEmbeddedCache", inMemory: { cacheSize: 104857600 // 100 MB、デフォルトは50MB } } ], logging: { level: "INFO" // ログの設定変更。DEBUGにするとより細かい情報を確認できます } }); // サーバの起動 engine.listen({ port: 4000, expressApp: app }); module.exports = engine;
これで Express.js を再起動して、画面をリロードしてみます。ターミナルで以下を実行してください。
# express-graphqlフォルダで npm run start を実行したターミナルでCtrl + C 。その上で再度以下を実行 npm run start
そうすると Apollo Engine にパフォーマンス情報などを送信するようになります。React側の画面にもう一度アクセスをしたあとに数秒おいて Apollo Engine をリロードしてみてください。
実際に GraphQL の API を運用する場合はこういった情報を使ってパフォーマンスを計測したり、必要に応じてリゾルバやキャッシュの改善を行う必要があると考えています。
GitHub レポジトリ
今回の チュートリアルのコードを GitHub に公開しています。良ければ参考にお使いください。
https://github.com/m3dev/graphql-apollo-sample
今後の課題
今回のチュートリアルでは GraphQL の API を簡単に構築できることを紹介しました。実際に本番で運用する場合には次のような課題を検討・解決していく必要があると考えています。
- AWS App Sync のような仕組みを使うべきか、自前で GraphQL アプリを開発すべきか?
- 不特定多数のクライアントに GraphQL の API を公開する場合は、クエリの実行権限管理や事前にクエリのコストを見積もる仕組みの整備が必要
- Web 画面のフォームのバリデーションと、GraphQL 側のバリデーションをどう整合性を取るか?などのアプリ開発での実践的なノウハウ
海外で一緒に働いてもらえるエンジニアを募集中
M3 ではアメリカやイギリス(EU)のビジネスの拡大に伴い、一緒に働いてもらえるエンジニアを募集しています!
また社内や グループ会社の メビックス でもGraphQL や Vue.js を使ったプロジェクトが着々と進行中です!GraphQLの開発に興味がある方もぜひ エンジニア向けフォーム からお気軽にご連絡ください!
GraphQL 関連のおすすめサイト
Apollo Blog
Apllo を開発しているチームの Blog (英語)です。セキュリティやキャッシュ、負荷対策など GraphQL を本番で使うために必要な実践的な知識を紹介してくれており、大変参考になります。
おまけ:便利そうなサービス・ツール
その他、GraphQL を調査している段階で面白そうなサービスをいくつか見つけたのでその紹介です。
Prisma & graphql-yoga
- Prisma
- graphql-yoga
- Tutorial
Postgraphile
PostreSQL のフロントで GraphQL を提供してくれる OSS、既存の DB に対して簡単に GraphQL を提供できるのが面白い。
参考リンク
- REST 2.0 Is Here and Its Name Is GraphQL
- GraphQL は REST の置き換えではない|こんぴゅ| note
- GraphQL の話 (API for Front-end) // Speaker Deck
- GraphQL は何に向いているか - k0kubun's blog
- GraphQL API を Rails アプリに実装した時のメモ | qnyp blog
- REST API に疲れたあなたへ贈る GraphQL 入門
- GraphQL でのパフォーマンス関連の問題について色々紹介する
- Prisma.ioでGraphQL APIサーバーを楽して作る - Qiita
- GraphQLを使ったAPI仕様中心開発の導入とその効果の紹介 - Kaizen Platform 開発者ブログ
- 世のフロントエンドエンジニアにApollo Clientを布教したい - Qiita
- GraphQL for Ruby