GraphQLを試してみた1

# GraphQLを試してみた

GraphQLのことを初めて知ってから1年ぐらい経つが、
先日イベントで聞いた際に興味を持ったので今更ながら試してみた。
本稿はそのまとめ1である。

## RESTとGraphQL

RESTは考え方で、GraphQLはクエリ言語のため、比較するのもおかしいが、
触った印象としては以下の感じ。

* RESTの場合、複雑なクエリを扱う場合に複数回のデータアクセスが必要になるが、1度のアクセスで取得できる (最初はこれぐらいしかメリットを感じてなかった)。
* RESTよりもIF設計の自由度は低いが、データモデルだけを気にすればよいため、設計の手間が少ない。
* RESTよりもドキュメントやスタブを作る手間が低い (Swagger要らず)。
* GraphQLの場合、スキーマの定義で必須項目や型を定義できる( https://graphql.org/learn/schema/ )。

## サンプル

Node.jsで実際に書いてみたサンプルが後述の通り。
基本的に以下のページの情報だけで十分実装できた。

https://graphql.org/

### サーバ側

使うライブラリやフレームワークはnpmでインストールする。

```$ npm install express express-graphql graphql```

```javascript:server1.js
let express=require('express')
let express_graphql = require('express-graphql');
let { buildSchema } = require('graphql');

let app = express();
app.use('/graphql', express_graphql({
    // スキーマの定義
    schema: buildSchema(`
      type Query {
        id: String
      }
    `),
    // サーバ側の値
    rootValue: {
      id: "hoge"
    }
}));

app.listen(3000, ()=> console.log('Listening on http://localhost:3000/graphql'));
```

### クライアント側

クライアント側は主にcurlで試した。
アクセスする方法としては以下の2パターンある(ライブラリによる？)。

```shell-session
$ curl -H "Content-Type:application/graphql" -X POST http://localhost:3000/graphql -d 'query { id }' 
{"data":{"id":"hoge"}}
```

JSON形式の場合、以下のように「{ "query" : "XXXXX"}」のような形式でデータアクセスを行う。

```shell-session
$ curl -H "Content-Type:application/json" -X POST http://localhost:3000/graphql -d '{ "query" : "query { id }" }'                                                                                                                   
{"data":{"id":"hoge"}}
```


## 固定値以外を返却する場合

### サーバ側

固定値以外を返却する場合、スキーマと処理を以下のように記載する。

```javascript:server2.js
let express=require('express')
let express_graphql = require('express-graphql');
let { buildSchema } = require('graphql');

let app = express();
app.use('/graphql', express_graphql({
    schema: buildSchema(`
      type Query {
        getUser(id:Int) : String
      }
    `),
    rootValue: {
      getUser:function(args){
        console.log(args);
        return "uid:" + args.id;
      }
    }
}));

app.listen(3000, ()=> console.log('Listening on http://localhost:3000/graphql'));
```

### クライアント側

固定値同様、2パターンの例を示す。

```shell-session
$ curl -H "Content-Type:application/graphql" -X POST http://localhost:3000/graphql -d '{ query : getUser(id:1234) }'
{"data":{"getUser":"uid:12345"}}
```

```shell-session
$ curl -H "Content-Type:application/json" -X POST http://localhost:3000/graphql -d '{ "query" : "query { getUser(id:12345) }" }'
{"data":{"getUser":"uid:12345"}}
```

また、GraphQLの強みの1つでもある、複数のクエリを同時に行う場合は以下のように記載する。

```shell-session
$ curl -H "Content-Type:application/json" -X POST http://localhost:3000/graphql -d '{ "query" : "query { a:getUser(id:1234)  b:getUser(id:2234) }" }'
{"data":{"a":"uid:1234","b":"uid:2234"}}
```

上述のように同じクエリを複数実行する場合はalias機能を使用する。
「エイリアス名:クエリ」のように記載することで、同じクエリで引数の異なるものをまとめて扱うことができる。

## 型定義

独自の型を定義する場合の記載は以下の通り。

### サーバ側

```javascript:server3.js
let express=require('express')
let express_graphql = require('express-graphql');
let { buildSchema } = require('graphql');

let app = express();
app.use('/graphql', express_graphql({
    schema: buildSchema(`
      type User {
        id:Int
        name:String
      }
      
      type Query {
        getUser(id:Int) : User
      }
    `),
    rootValue: {
      getUser:function(args){
        console.log(args);
        let user = {
          id: args.id,
          name: "Mike"
        };
        return user;
      }
    }
}));

app.listen(3000, ()=> console.log('Listening on http://localhost:3000/graphql'));
```

### クライアント側

```shell-session
$ curl -H "Content-Type:application/graphql" -X POST http://localhost:3000/graphql -d 'query { getUser(id:1234){name} }'
{"data":{"getUser":{"name":"Mike"}}}
```

```shell-session
$ curl -H "Content-Type:application/json" -X POST http://localhost:3000/graphql -d '{ "query" : "query { getUser(id:1234){ id, name }}" }' 
{"data":{"getUser":{"id":1234,"name":"Mike"}}}
```
nameだけ取得する場合は以下。

```shell-session
$ curl -H "Content-Type:application/json" -X POST http://localhost:3000/graphql -d '{ "query" : "query { getUser(id:1234){ name }}" }' 
{"data":{"getUser":{"name":"Mike"}}}
```

## おまけ

### 図の作成

GraphQLの場合、graphqlvizを使用して図を簡単に作れる。
(他にも探せばいろいろある？)
インストールはnpmで可能。

```shell-session
$ npm install -g graphqlviz
```

```shell-session
$ graphqlviz -v http://localhost:3000/graphql | dot -Tpng -o graph.png
```

![graph.png](https://qiita-image-store.s3.amazonaws.com/0/263164/01ccdc25-e546-d4ae-4b3c-de2506451dd8.png)

また、スキーマ定義を別ファイルにすることで以下のような方法でも可能。

```shell-session
$ graphqlviz -g test.gql | dot -Tpng -o graph.png
```

```graphql:test.gql
type User {
  id:Int
  name:String
}

type Query {
  getUser(id:Int) : User
}
```

尚、Node.jsでスキーマ定義を別ファイルから行う方法は見つけられなかった…。
(以下のような方法でも実行できることは確認したが)

```javascript
buildSchema(fs.readFileSync('./test.gql', 'utf8'))
```

### コメントとdescriptionを追加

GraphQLのスキーマ定義では以下のように「#」を付けてコメントを記載できる。

```graphql:comment.gql
type User {
  # User ID
  id:Int
  name:String
}

type Query {
  getUser(id:Int) : User
}
```

また、以下のように記載することでドキュメンテーション文字列(Javaのjavadocのようなもの)を記載できるようだ。

```graphql:desc.gql
"""
ユーザ情報
"""
type User {
     "ユーザID"
     id: Int
     " 名前"
     name: String
}

type Query {
  getUser(id:Int) : User
}

```