PythonでGraphQLをやろうとした時につまづいたこと

今まではRESTFulなAPIでいろいろ実装していたものの、APIが様々なサーバーに散らばっていることや問い合わせ回数などが気になり始め、これらを解決するためにGraphQLを取り入れていこうと思っていました。
学習を進めるうちにわからないことなどが多くでてきたため、躓いた箇所をメモって行こうと思います。

## GraphQL on Python

Pythonで使えるライブラリは [Graphene](https://github.com/graphql-python/graphene) ほぼ一択なのではと思っています。他には特に探していません。
自社では使用しているDBMSがSQLServerであるという制約から、ORMは [SQLAlchemy](https://github.com/zzzeek/sqlalchemy)。重いフレームワークは不要ということで、Webフレームワークは [Flask](http://flask.pocoo.org/) を選択しました。

## ステップ1. 環境整備
### 基本環境
* MacOS Sierra 10.12.6
* Homebrew 1.5.11
* virtualenv 15.1.0
* Python 3.6.1

### requirements.txt
```
aniso8601==3.0.0
click==6.7
Flask==0.12.2
Flask-GraphQL==1.4.1
graphene==2.1
graphene-sqlalchemy==2.0.0
graphql-core==2.0
graphql-relay==0.4.5
inflect==0.2.5
iso8601==0.1.12
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
promise==2.1
pyodbc==4.0.23
Rx==1.6.1
singledispatch==3.4.0.3
six==1.11.0
sqlacodegen==1.1.6
SQLAlchemy==1.2.6
typing==3.6.4
Werkzeug==0.14.1
```

## ステップ2. チュートリアル
Graphene-Pythonの [チュートリアル](http://docs.graphene-python.org/projects/sqlalchemy/en/latest/tutorial/#) を見ながら動作を試します。
（自社のデータのスキーマに合わせて色々変えたりします）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/105394/1634f627-d824-ca23-fb3c-1650bd05c6b9.png)

チュートリアルどおりにやると、動くは動くのですが、わからないことが幾つかでてきます。

### Connectionとはなにか

最初のチュートリアルにedge、nodeというキーワードが出てきます。[(*)](http://docs.graphene-python.org/projects/sqlalchemy/en/latest/tutorial/#testing-our-graphql-schema)
どうやらそれらは、GrapheneのConnectionというオブジェクトが生成しているようです。

[Graphene-Connection](http://docs.graphene-python.org/en/latest/relay/connection/)
Grapheneにおいて、ConnectionとはListを拡張して、スライスとページネーションの機能を提供すると書いてあります。
最初はイメージがわかずだいぶ苦労しましたが、下記の記事を見てだいたい理解しました。

[GraphQL入門 - 使いたくなるGraphQL](https://qiita.com/bananaumai/items/3eb77a67102f53e8a1ad)
[GraphQL Server Specification](https://facebook.github.io/relay/docs/en/graphql-server-specification.html#connections)

つまり、GraphQLのConnectionはオブジェクトを再取得するための仕組みを提供してくれているみたいです。
頻繁に追加されうるデータや、数が多いデータについてはListではなくConnectionにしておいた方が良さそうです。

## ステップ3. Connectionを実用として使うには

Connectionはページネーションの仕組みを組んでくれているのは助かるのですが、より細かにfilterを作ろうと思うと、grapheneが標準で提供してくれている機能では十分ではないと感じました。

主にやりたいことを挙げますと

* 値による比較（完全一致、範囲、文字列パターン、など)
* 順序の指定

これについては、GrapheneのConnectionを継承して拡張するしか無さそうです。StackOverflowで、クエリ設定して拡張する方法、FilterQueryオブジェクトを定義する方法など、いろんな回答がありました。
[How to filter and limit? ](https://github.com/graphql-python/graphene-sqlalchemy/issues/27)

上記にはAlchemyのModelを全て公開する形でfilteringできるようなコードもあるんですが、流石にそれは嫌だったりしますｗ。Connection毎に定義できる仕組みを作る必要がありそうです。

ちなみにDjangoでやる場合は、Django-filterというライブラリである程度までできるみたいです。
SQLAlchemyではどのように実装するのがベストかよく分かりませんでしたので、発明するか発見するかしないといけないようです。誰かいい方法教えてください。

## ステップ4. Variableの定義について
Grapheneでは自由にメソッドっぽいクエリを書けます。

例）

```graphql
query {
  getUser(name: "Hoge") {
    user {
      id
      name
    }
  }
}
```

これはGraphene的にはQueryに足したいFieldを追加するだけで実装できるのですが、ここのいい設計方法無いかなあと探しています。
（現状GithubのAPIが一番いい気がします）

## ステップ5. Mutationについて

これは仕組みが簡単でした。ある程度内部の作り込みが必要なのは仕方がないと思います。


## ステップ6. セッション・権限

セッション、権限周りはFlaskなど上位レイヤーが頑張るのが良いのかな。
可能ならば、アカウントの権限ごとにエントリポイント分けてしまえれば望ましいと感じました。

自分の場合は色々あって下記のような構成にしました。

```
graphql-application
 |
 --- schema
 |
 --- admin-schema
 |
 --- model
 |
 app.py
```

## まとめ

当初、「GraphQLにしたらAPI作るの簡単だヨ！」って布教していくつもりでしたが、取り組んでみてからは「なんだこれ結構大変だぞ」と思うことも多い気がします。
GraphQL自体をある程度勉強してからでないと、つらいですね。
勉強しよ…

[Draft](http://facebook.github.io/graphql/October2016/)
[GraphQL勉強会資料](https://qiita.com/n-oshiro/items/d294dac42df7b70a73f9)