GolangでGraphQLサーバを組む際に必要になりそうなチェックリスト

1.12時代に書いたメモなので2019-12-06現在最新ver(1.13)の状態には合わないかもしれないけど一応忘備録として公開しておく

このメモに書いた内容はGitHubにboilerplateとしてまとめてある

- [shufo/go\-graphql\-boilerplate: A boilerplate for creating GraphQL server with Go](https://github.com/shufo/go-graphql-boilerplate)

ここで挙げられたライブラリの基本的な選定の基準としては一つのライブラリであまり多くのことをやろうとせずUNIX哲学的にシンプルに一つの責務のみをこなし他ライブラリとのinteroperationがしやすいものを選ぶという感じ

## GoでGraphQLサーバを実装する上で必要になりそうなチェックリストとその対策

- [ ]  GraphQL
- [ ]  ORM
- [ ]  認証
- [ ]  認可
- [ ]  ルーティング
- [ ]  DB
- [ ]  バリデーション
- [ ]  セキュリティ (CSRF)
- [ ]  マイグレーション
- [ ]  ロギング
- [ ]  テスト (E2E, Unit)
- [ ]  ドキュメンテーション
- [ ]  ページング
- [ ]  モジュール化
- [ ]  Fixture
- [ ]  キューイング
- [ ]  ホットリロード
- [ ]  i18n
- [ ]  パッケージング
- [ ]  デプロイ

## 環境

- go 1.12
- gqlgen
- chi

### Router

[Chi](https://github.com/go-chi/chi)を使う
gqlgen公式でサンプルとして紹介されているが標準インターフェースのhttpパッケージでやり取りしてるので相互運用性がよい

### GraphQL

[gqlgen](https://github.com/99designs/gqlgen)を使う

そのまま使うとN+1クエリが出てしまうので[dataloaden](https://github.com/vektah/dataloaden)でクエリをまとめる。[仕組み](https://gqlgen.com/reference/dataloaders/)としてはdataloadenが1msほどwaitしてクエリをまとめてN+1クエリをIN句を使ったクエリに変換している

### ORM

[sqlboiler](https://github.com/volatiletech/sqlboiler)を使う

### 認証

JWTで行う
chiのmiddlewareの[jwt-auth](https://github.com/go-chi/jwtauth)を使う
今回はトークンのRevokeは要件にないので一旦は問題なし

### 認可

JWTは本人性の確認のみなので認可は別途実装する必要がある
おおざっぱな認可はGqlgenのdirectiveを使う
詳細な認可は[casbin](https://github.com/casbin/casbin)でRBACで認可を行う

```graphql
@hasRole(role: ADMIN)
```

問題: 管理画面系のクエリ、Mutationはどうするか
`/query`という同一のエンドポイントで管理画面用のクエリを別に用意する？
クエリ一覧がわかりづらくならないか

```graphql
type Query {
  hero(id: Int): Hero
  droid(id: Int): Droid
}
```

### ルーティング

GraphQLなので基本的には必要ないが一部分APIにしたいというような場合はchiのルーティングを使う

### DB 

ORMは[sqlboiler](https://github.com/volatiletech/sqlboiler)を使う

### バリデーション

GraphQLの型チェックはする
+カスタムのバリデータとして[ozzo-validation](https://github.com/go-ozzo/ozzo-validation)など使う

出来れば宣言的にバリデーションしたいがsql-boilerが動的にタグを生成していじってはいけないのでタグに埋め込む形のバリデータと少し相性が悪い

各modelのStructにValidateメソッドを生やしてその中で各フィールドのバリデーション、任意のタイミングでValidateメソッドを実行

### セキュリティ

chiのmiddlewareを使う

- [cors](https://github.com/go-chi/cors)

### migration

[sql-migrate](https://github.com/rubenv/sql-migrate)を使う
→ [packr](https://github.com/gobuffalo/packr) でのマイグレーションファイルのマウントに対応してたので

これによりシングルバイナリでサーバにポン置きでmigrationを実行出来る

### ロギング

[logrus](https://github.com/sirupsen/logrus)でJSON形式で出力

### テスト (E2E)

unitは[testify](https://github.com/stretchr/testify)
graphqlクライアントとして[go-graphql](https://github.com/machinebox/graphql)でリクエストする
E2Eはdreddなど別途ツールを使ってもいいかも

### ドキュメンテーション

GraphQLのIntrospectionで提供
開発サーバにplaygroundを埋め込み
本番環境では無効にする

### ページング

Cursor-based pagination

### モジュール化

go 1.11より導入された Go module で管理

### Fixture

事前に作成したSQLを直接読み込ませるか
https://github.com/zhulongcheng/testsql

gp-sqlmockで仮想的にmockデータを作りそれでやりとりさせるか

### キューイング

外部にキューイング用のバックエンドを用意する想定

redisをバックエンドに[machinery](https://github.com/RichardKnop/machinery)を使う

### ホットリロード

[realize](https://github.com/oxequa/realize)

2019-03-21現在 Go 1.12のGo Modulesに対応していなかったのでgo.modファイルを使う場合は多少設定ファイルをいじる必要がある

### i18n

[go-i18n](https://github.com/nicksnyder/go-i18n)で多言語化対応

### パッケージング

[packr](https://github.com/gobuffalo/packr)を使いマイグレーションファイルなどの静的ファイルをバイナリにembedする

### デプロイ

packrでシングルバイナリにしたあとalpineイメージにビルド後のバイナリをADDしてデプロイ用のイメージ作成
scratchでもいいけどなんだかんだでコンテナ内でapkコマンド叩けると嬉しい場面が多いのでそこまでシビアにイメージサイズ絞りたいとかでなければalpineで問題ないと思う