LoginSignup
19
17

More than 5 years have passed since last update.

ゼロからGraphQL (ゼロ知識からDBなし最小構成でサーバを動かしてみる)

Last updated at Posted at 2018-10-11

GraphQLとは=「データを柔軟に取り出せるAPI:bar_chart:

大崎です。今回はできるだけ簡単にGraphQLを説明して試します。触ってみたいだけという方はライブデモがあります。

GraphQLは、APIの形式です。Facebookが開発、Mercariがマイクロサービス化するためにバックエンドのAPIとして採用しているそうです1
image.png

でも現在のデファクトのAPI形式はRESTです。なので、よくRESTと比べられます。

ですから先に結論を言っておくと、できることはRESTと大きくは違いません:relaxed:
ただ、ちょっとデータ取得特化型の柔軟性(クエリが書けるという大きな特徴):thumbsup:を持っています。

APIってそもそも

APIができることは、データを取ってくるだけでなく、以下のようにいろんな仕事ができるのですが、、、

  • トランザクションを無事こなす:pencil2:
  • 非同期処理をスタートする:timer:

ですが、今回はデータ取得:bar_chart:に話を絞ります
(GraphQLはデータ更新のしかけMutationも持っていますがあまり大事ではないので省略します)

RESTの課題:デフォルトでは柔軟性がついてない

さて、ある目的のためにデータが欲しいとき、APIが提供してくれるデータでは過不足が生じます。

  • 目的例:triangular_flag_on_post:
    「近くのレストランの名前を評価順に5個取得する」

  • 提供されているREST API http://<URL>/restaurants/
    市内の全レストランの一覧:worried:
    それぞれに場所名前オーナー電話番号と・・・・・・と従業員を取得する:confounded:

5個だけ取るつもりが1000個くらいデータが取れるし、無駄な値がくっついてくる。
時間もかかる。
欲しいものだけを取り出す、ということで思い出すのは下記例文にもあるSQLのようなクエリ言語ですが、残念ながらSQLはAPIとして提供されません。

select restaurant_name from all order by grade limit 5;

ODataというSQL over HTTP API形式がありましたが、「危険」という声がありました:u7981:2
SOAPで書けるかもしれません。でもXML書くのめんどくさい:thermometer_face:

GraphQLの柔軟性=「欲しいデータだけを要求できる:tools:

GraphQLなら、柔軟性があるしデータ形式がRESTと同じJSONで使いやすいです。

RESTのPOSTでクエリを入力するやり方しかないかな、、、と思っていたところに出てきたGraphQL
完璧とはいえないかもしれません。だってクエリの書き方がちょっと見慣れないですし。

{
  restaurants(first: 5, sort: "grade") {
    name
  }
}

でも、このクエリをカスタマイズすると、データの要求を柔軟に変更し、JSONでデータが取れます
柔軟性の部分は、詳しくは試しながら説明します。

返り値がJSONなので、Nodeなどで充実したいろんなツールのパワーをフル活用できます。
RESTからデータを取ってくるのと同じようにアプリが書けます!

最小構成でいいからまずはGraphQLを使う

最小セットのGraphQLサーバ=「単なるWebサーバ(DBなし)」

今回はほぼ、素のGraphQLサーバです。

データは日本の市のリストを使います。といっても三鷹市と香取市の2つの市しか入っていません。

機能は一覧を取得する、という1個しかありません。
まだクエリらしいクエリは書けません。
でもまず動かします。

サーバの機能は下記3つです。

  • データを保持 (1)
  • クエリを受け付け (2)
  • クエリにあわせたデータを返す (3)

(1)は何もしなくていいです。いったん仮データをJSONで入れるだけ。
(2)は、apollo-servergraphql-yogaなどのGraphQLのありがたいサーバが全部やってくれます
(3)が大事で、「どんなクエリにどんな値を返す」という設定を書くのがresolverです。

準備

今回はTypescriptでアプリを作ります。

環境にnpmtscが入っていることを前提とします。

今回のGraphQLサーバアプリの名前を付けたフォルダを作ります。npm initでnode.jsの初期化をします。
graphql-yogaは今回使うGraphQLサーバライブラリです。
ほかにもapollo-serverなどがあります。
あとは3つくらい起動用のツールを入れます。

$ mkdir sample_app
$ cd sample_app
$ npm init
### 途中のmain指定では index.jsではなくてindex.tsを指定してください。
$ tsc --init
$ npm install graphql-yoga --save
$ npm install nodemon ts-node typescript --save-dev

package.jsonscriptsstartの行を追加します。package.jsonが以下のようになるといいです。

package.json

{
  "name": "simple_json_graphql",
  "version": "0.0.1",
  "description": "Simple GraphQL Server from JSON data",
  "main": "index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon --ext ts,yaml --exec 'ts-node' index.ts"
  },
  "author": "Hiroyuki OSAKI (https://github.com/onelittlenightmusic)",
  "license": "ISC",
  "dependencies": {
    "graphql-yoga": "^1.16.2"
  },
  "devDependencies": {
    "nodemon": "^1.18.4",
    "ts-node": "^7.0.1",
    "typescript": "^3.1.1"
  }
}

index.tsを作ります

さきほど説明した(1)から(3)を一行ずつ書いてみます。

index.ts
import { GraphQLServer } from 'graphql-yoga'

// (1)データを保持
const locations = [
  {Japanese: "三鷹市",
  Prefecture: "Tokyo",
  Population: 180797},
  {Japanese: '香取市',
  Prefecture: 'Chiba',
  Population: 85193},
]

// (2)クエリを受け付け
const typeDefs = `
    type Location {
      Japanese: String
      Prefecture: String
      Population: Int
    }
    type Query {
      locations: [Location]
    }
`;

// (3)クエリにあわせたデータを返す
const resolvers = <any>{
  Query: {
    locations: () => locations,
  },
};

const server = new GraphQLServer({ typeDefs, resolvers })
server.start({port: 4040}, () =>
 console.log(`Your GraphQL server is running now ...`),
)

順に説明します

(1)は、べた書きでデータの配列を入れています。
だからDBは使っていません。
このデータはWikipedia3からとってきたもので、PythonでスクレイピングしてJSON化したものです。Pythonスクレイピングはこちらを参照


// (1)データを保持
const locations = [
  {Japanese: "三鷹市",
  Prefecture: "Tokyo",
  Population: 180797},
  {Japanese: '香取市',
  Prefecture: 'Chiba',
  Population: 85193},
]

(2)は、データの型Locationとクエリlocationsを作っています。locationsクエリがリクエストされると配列[Location]が返るということを意味しています。
GraphQLの型とスキーマの詳しい説明は本家ドキュメントがおすすめ。


// (2)クエリを受け付け
const typeDefs = `
    type Location {
      Japanese: String
      Prefecture: String
      Population: Int
    }
    type Query {
      locations: [Location]
    }
`;

(3)は、クエリで返す値は、locations、つまり(1)の値全部ですよ、と言っています。
このように、GraphQLサーバ実装のメインの仕事は、resolverに処理を書くことです。


// (3)クエリにあわせたデータを返す
const resolvers = {
  Query: {
    locations: () => locations,
  },
};

サーバを起動します


$ npm start

GraphQLサーバがlocalhost:4040で待機すると思うので、ブラウザでアクセスしてください。

graphql-yogaには、便利なクエリ実行GUIであるgraphql playgroundがついていて、デフォルトで起動することになっています。
その画面が見えるはずです。

image.png

クエリを実行してみます

GraphQLのクエリはこんな風に書きます。
graphql playgroundの左側のクエリ編集ペインに打ち込みます。


{
  locations {
    Japanese
    Prefecture
    Population
  }
}

打っていて気付いたと思いますが、超強力な補完機能がついています。構文ミスも型不整合もチェックしてくれます。

では、真ん中の:arrow_forward:ボタンを押してみます。

結果は出ましたか?

image.png

(1)で入れたデータがそのままJSONで出てきましたね!

柔軟性(欲しいデータだけ要求する)を確認する :wrench:

ではクエリを変えてみます。
人口データなんかいらない、市の名前と県名だけでいい、という場合を想定します。

#Populationの前にいれてコメントアウトしてみます。

{
  locations {
    Japanese
    Prefecture
    # Population
  }
}

実行してみます。

image.png

前のクエリでは人口データが入って返ってきていたのですが、今回は入っていません。
つまり、要求した2つのフィールドJapanese(市の名前)とPrefecture(県名)だけが返ってきている ことがわかると思います。

GUIからではなくそのままAPIとしても使えるので、このGUIで試しながら、APIを使うアプリを実装できます。

ちなみに、全部の日本の市が入ったGist上のデータ(自作)を参照するように改造するとGithubレポジトリのような形になります。
クエリも一部追加自作していますので良ければご覧ください。

ライブデモ

Herokuに公開し、GraphQLPlaygroundをgraphqlbin上で公開しました。

日本の市を一覧化し、人口、設立日など統計値も加えました。

image.png

実はtypeにコメントを書くと、自動補完のときや右のschemaペインを参照したときに型の情報も表示してくれるんですね!便利です。

image.png

総務省からは市区町村コードも公開されていますので、これも随時GraphQL化したいと思います。

総務省/全国地方公共団体コード

他の参考文献

https://qiita.com/hassaku_63/items/d12d36d17cce536ade66
Apollo-server版最小構成デモ

19
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
17