【gqlgen】GoでGraphQLに触れてみる

本日もお疲れ様です。今回はRestAPIに対をなすべく登場したGraphQLをそろそろ触ってみようの記事です。

完全に学習日録記事ですが、それゆえ難しい内容は出てこない(書けない)ので同じくさらっと知っておきたい方には見ていただけるとありがたいです。

今回はチュートリアルもわかりやすくすぐに始められそうなgqlgenを利用して入門してみることにします。

GraphQL is 何?

ここは今流行りの生成AIのうちの一つであるperplexity.ai先生によると

GraphQLは、APIのためのクエリ言語およびランタイムです。以下にGraphQLの主な特徴をまとめます:

1. 単一エンドポイント: GraphQLは1つのAPIエンドポイントを通じてすべてのデータにアクセスできます。
2. クライアント主導: クライアントが必要なデータの構造を定義し、サーバーは定義された通りのデータを返します。
3. 効率的なデータ取得: 必要なデータのみを取得でき、オーバーフェッチやアンダーフェッチの問題を解決します。
4. 強い型付け: スキーマを使用して、利用可能なデータの型を定義します。
5. 柔軟性: 既存のクエリに影響を与えずにAPIを進化させることができます。
6. パフォーマンス: 1回のリクエストで複数のリソースを取得できるため、効率的です。
7. 自己文書化: スキーマを通じてAPIの構造が明確になり、ドキュメンテーションが容易です。

GraphQLは、FacebookによってRESTの制限を克服するために開発され、複雑なシステムやマイクロサービスアーキテクチャに特に適しています。クエリ、ミューテーション、サブスクリプションの3つの主要な操作をサポートしており、Web開発において効率的でフレキシブルなデータ取得を可能にします。

この後実際に触ってみるとわかりますが、REST APIと比較した際に特徴的なのはスキーマをを利用したリクエストだと思います。

そのため上記にもある通り、必要なデータのみを取得できたりデータの型を定義できたりするというわけです。

ついでにGraphQLを構成するコンポーネントも確認しておきましょう。

GraphQLを構成する主要なコンポーネントは以下の通りです:

  1. スキーマ (Schema)
    スキーマはGraphQL APIの設計図のようなもので、利用可能なデータ型、フィールド、操作を定義します
  2. タイプ (Types)
    スキーマ内で定義されるデータ構造です。オブジェクト型、スカラー型、列挙型などがあります
  3. フィールド (Fields)
    タイプ内の個々のデータ項目を表します
  4. クエリ (Query)
    データの読み取り操作を定義します。RESTのGETに相当します
  5. ミューテーション (Mutation)
    データの作成、更新、削除操作を定義します。RESTのPOST、PUT、DELETEに相当します
  6. サブスクリプション (Subscription)
    リアルタイムデータ更新を受け取るための操作を定義します
  7. リゾルバ (Resolver)
    各フィールドのデータ取得ロジックを実装する関数です
  8. 引数 (Arguments)
    クエリやミューテーションに渡すパラメータです
  9. フラグメント (Fragments)
    再利用可能なクエリの一部を定義できます
  10. ディレクティブ (Directives)
    クエリの実行方法を制御する特別な指示です。@includeや@skipなどがあります

これらのコンポーネントを組み合わせることで、柔軟で効率的なAPIを構築できます。GraphQLの特徴である型システム、単一エンドポイント、クライアント指定のデータ取得などが、これらのコンポーネントによって実現されています

これらのコンポーネントを事前に頭に入れておくとこの後の内容が入ってきやすいです。

それでは実際にgqlgenを利用してGraphQLの世界に飛び込んでみたいとおもいます。

準備

ここからは基本的にGetting Startedを元にして執筆しております。もし、手も動かしたいという方がいらっしゃいましたらGetting Startedも合わせて開いておいていただけるとスムーズかと思います。

まずはGetting Startedにそって準備を進めてください。支持されているコマンドを入力すると一通りのファイルが作成されるはずです。

もし余裕があればハンズオンまで一通り進めてもらえればと思います。

ここから各ファイルを見ながらGraphQLについて理解を深めていきます。

スキーマ

GraphQLを利用する上で必ず理解しておくべきものの一つがスキーマです。

Getting Startedによると

When executed, gqlgen’s generate command compares the schema file (graph/schema.graphqls) with the models graph/model/*, and, wherever it can, it will bind directly to the model.

とあります。

GraphQLで利用するスキーマはschema.graphqlsに定義、generateコマンドを利用することで現状のmodelと比較しserver側で利用できるよう差分をmodelとして自動生成してバインドしてくれます。(あるいは自動削除)

つまりgqlgenを利用するにしろ基本的にはschema.graphqlsにてスキーマを確認しておけばいいということになります。

実際にschema.graphqlsの内容は

type Todo {
  id: ID!
  text: String!
  done: Boolean!
  user: User!
}

type User {
  id: ID!
  name: String!
}

type Query {
  todos: [Todo!]!
}

input NewTodo {
  text: String!
  userId: String!
}

type Mutation {
  createTodo(input: NewTodo!): Todo!
}

となっています。各オブジェクトにはフィールドと型が定義されていますね。Queryはデータの読み込み、Mutationがその他のデータ操作を行うということが分かれば何となく理解はできそうです。

先ほどの準備でmodelの作成まではすでに終わっているはずなのでスキーマの準備は完了です。

リゾルバ

リゾルバは各フィールドのデータ取得ロジックを実装する関数です

今回の場合、基本的にロジックが定義されているのはschema.resolvers.goです。

func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
	randNumber, _ := rand.Int(rand.Reader, big.NewInt(100))
	todo := &model.Todo{
		Text:   input.Text,
		ID:     fmt.Sprintf("T%d", randNumber),
		UserID: input.UserID,
	}
	r.todos = append(r.todos, todo)
	return todo, nil
}

func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
	return r.todos, nil
}

query, mutationリクエストを行った場合の実際の処理がここに定義されています。今回のハンズオンではDBは利用しないので代わりとなる配列をresolver.goに定義しています。

type Resolver struct {
	todos []*model.Todo
}

Todo modelを引き受ける構造体です。実態はmodel/todo.goに定義されています。

package model

type Todo struct {
	ID     string `json:"id"`
	Text   string `json:"text"`
	Done   bool   `json:"done"`
	UserID string `json:"userId"`
	User   *User  `json:"user"`
}

今回の場合、リゾルバはこのmodelに対してデータの作成(mutation)や呼び出し(query)を行っているということになります。

サーバー

実際にクエリを受け取って動作するにはサーバーとして起動させる必要があります。

server.goがすでに用意されているのでコマンドを利用して起動するだけです。

$ go run server.go
2024/09/24 10:56:41 connect to http://localhost:8080/ for GraphQL playground

上記の文章が出力されたら実際に接続してみてください。GraphQLを試すことができます。

実行するクエリはGetting Startedに記載されているものを利用してください。

まとめ

今回はGo(gqlgen)を利用してGraphQLに入門してみました。

スキーマを定義、スキーマにそってmutationやqueryリクエストを実行するとサーバー側のリゾルバが解決してレスポンスを返すというのが超大まかな流れになりそうです。

かなりざっくりとした内容で商用知識とは程遠いですが、大まかな特徴は捉えることが出来たかなと思っております。

gqlgenについても実用レベルにするには全然まだまだ深掘る必要がありそうですが、基本的な利用方法は把握できました。

私はインフラSREエンジニアなので普段コードを書くことは少ないですが、GraphQLを利用するプロジェクトに参画する心の準備が整ったと思いたいですね。

最後まで読んでいただきありがとうございました!

Golang

Posted by CY