GraphQL Serverを実装してみた

# GraphQL Serverを実装してみた

次のプロジェクトで _GraphQL API_ を使おう!という話になったので、
GraphQL ServerをGolangで試しに作成してみました。

Githubにサンプル実装のソースをコミットしましたので、ご参考下さい。

[tochukaso/graphql-server-sample]

## GraphQLとは

[GraphQL]とは、一言でいうと、

**SQL感覚で書けるAPI** である

[graphql.org]によると、以下の通り

``` text
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data.
GraphQL provides a complete and understandable description of the data in your API,
gives clients the power to ask for exactly what they need and nothing more,
makes it easier to evolve APIs over time, and enables powerful developer tools.
```

GraphQLは、APIのためのクエリー言語であり、また、そのクエリーを実行するためのランタイムです。
GraphQLはクライアントに正確で無駄のない要求を行えるようにし、APIの進化を簡単にします。

GraphQLを利用することで、具体的には以下の事ができるようになります。

- 取得したいデータをクライアント側から項目単位で指定出来る
  - REST APIではレスポンスの項目はサーバー側に決められていたが、GraphQLはクライアント側が指定する
- 一度のクエリーでSQLのJOINの様に複数のテーブルのデータを取得する事ができる
- 単一のエンドポイントに出来る
  - REST APIの場合、アクセス対象のエンティティ単位でエンドポイントが異なっていた
- リアルタイム通信(PUB/SUB)に対応している

_GraphQL_ の _QL_ は、_Query Language_ の略で、問い合わせをする言語という意味です。

SQLは _Structured Query Language_ の略で、GraphQLもSQL同様に問い合わせ言語となります。

クエリー（問い合わせ）として、以下の三種類が実行できます。

- query
  - データの取得処理を行う
- mutation
  - データの登録・更新・削除を行う
- subscription
  - リアルタイム通信を行う

GraphQLは2015年にFacebookが仕様を公開し、2018年11月にGraphQL Foundationに移譲された。

## GraphQL Serverの実装

Golangを使ってGraphQL Serverを実装しました。

- Go 1.16.5
- gqlgen 

Githubにサンプル実装のソースをコミットしました。

[tochukaso/graphql-server-sample]

GraphQL Serverの作成は以下の手順で行います。

1. スキーマファイルの作成
2. スキーマファイルをベースにコードの自動生成
3. resolver(各queryやmutation)の実装

### スキーマファイルの作成

スキーマファイルには以下の要素があります。

- type
  - データモデルの定義
- type Query
  - 問い合わせよう関数の定義
- type Mutation
  - 更新よう関数の定義
- type Subscription
  - サブスクリプション(リアルタイム通信)よう関数の定義
- input
  - Mutationよう関数の引数

※他にも _interface_,_enum_や_union_,_extend_ 等があります。

商品マスター(product) と商品のバリエーション(SKU)を表す定義は以下のように表現できます。

_schema.graphqls_ というファイル名で保存します。

``` text
type Product {
  id: ID!
  name: String!
  price: Int!
  code: String 
  detail: String
  createdAt: String
  updatedAt: String
  deletedAt: String
  skus: [Sku!]!
}

type Sku {
  id: ID!
  name: String!
  stock: Int!
  code: String
  createdAt: String
  updatedAt: String
  deletedAt: String
  product: Product!
}

type Query {
  products: [Product!]!
  product(id: ID!): Product!
  skus: [Sku!]!
}

type Mutation {
  createProduct(input: NewProduct!): Product!
  updateProduct(input: EditProduct!): Product!
  deleteProduct(productId: Int!): Int!
  createSku(input: NewSku!): Sku!
}

type Subscription {
  updateProduct(id: ID!): Product
}

input NewProduct {
  name: String!
  price: Int!
  code: String
  detail: String
}

input EditProduct {
  id: Int!
  name: String
  price: Int
  code: String
  detail: String
}

input NewSku {
  productId: Int!
  name: String!
  stock: Int!
  code: String
}
```

### スキーマファイルをベースにコードの自動生成

GraphQLのコードを、スキーマファイルから自動生成してくれるパッケージとして、[gqlgen]があります。

[gqlgen] を使用することで、GraphQL用のコードを自動生成することが出来ます。

gqlgen-generate ./...をルートディレクトリー直下(_gqlgen.yml_が置いてあるディレクトリー)で実行します。

読み込み対象のスキーマファイルや、モデルデータの作成有無、カスタムスカラータイプの設定などを _gqlgen.yml_で設定できます。

スキーマを変更したい場合も同様に、_schema.graphqls_ を変更後に、gqlgen-generate ./...を実行します。

schema.resolvers.go が作成、または変更されます。

### resolver(各queryやmutation)の実装

_schema.resolvers.go_ に新しく作成されるメソッドは、デフォルトは未実装( _panic_ となっている)となっています。

こちらのメソッドを実装していきます。

商品を新しく登録するメソッドは以下のように実装します。

DBのORマッパーとして、 [GORM]を使用しています。

``` go
func (r *mutationResolver) CreateProduct(ctx context.Context, input model.NewProduct) (*model.Product, error) {
	product := &model.Product{
		Name:   input.Name,
		Price:  input.Price,
		Code:   getString(input.Code, ""),
		Detail: getString(input.Detail, ""),
	}

	result := db.GetDB().Create(&product)

	if result.Error != nil {
		log.Print("商品の登録に失敗しました")
	} else {
		log.Print("商品を登録しました")
	}

	return product, result.Error
}
```

商品情報を取得するメソッドの場合、以下のように実装します。

``` go
func (r *queryResolver) Product(ctx context.Context, id string) (*model.Product, error) {
	var product *model.Product
	db.GetDB().First(&product, id)
	return product, nil
}
```

## まとめ
今回、Golangを使ってGraphQL Serverを実装する方法を紹介しました。GraphQLの特徴や利便性に触れながら、具体的な手順としてスキーマファイルの作成、コード自動生成、そしてResolverの実装までをカバーしました。GraphQLは従来のREST APIと比べて柔軟性が高く、クライアントが必要なデータだけを取得できる点が大きな魅力です。

Golangとgqlgenを利用することで、GraphQL Serverの構築がシンプルかつ効率的に行えることが分かりました。本記事がGraphQLの理解や導入の一助となれば幸いです。
## 参考

- [初めてのGraphQL O'Reilly Japan](https://www.oreilly.co.jp/books/9784873118932/)
- [graphql.org]
 
[GraphQL]: https://graphql.org/
[tochukaso/graphql-server-sample]: https://github.com/tochukaso/graphql-server-sample
[graphql.org]: https://graphql.org/
[gqlgen]: https://gqlgen.com/
[GORM]: https://github.com/go-gorm/gorm