📌

Go × GraphQL でサーバーを作ってみた

2023/05/23に公開

はじめに

Go言語と初挑戦のGraphQL を使って、GraphQL サーバーを作ってみたいと思います。
▼Go言語公式ドキュメント
https://go.dev/
▼GraphQL公式ドキュメント
https://graphql.org/

Goライブラリのgqlgenを使っていく

Go x GraphQL を両方学べるライブラリがありました。
その名もgqlgen(読み方は全くわからない・・)

公式ドキュメントにはこのように書いてありました。
(・_・D フムフム良くわからん。スキーマファーストってなんや。

gqlgenは、GraphQLサーバーを簡単に構築するためのGoライブラリです。
gqlgenはスキーマファーストのアプローチに基づいています

とにかくよくわからないので、手を動かしてみます。
以下のgqlgen公式ドキュメントに沿って環境を準備します。
https://gqlgen.com/

環境構築

環境構築はドキュメントを参照しつつ、進めます。
まずは、 Go のプロジェクトを作成します。

$ mkdir go-project
$ cd go-project
$ go mod init go-project

これで Go プロジェクトが出来ました。
プロジェクト内には、go.modファイルができています。

tools.goファイルを作成し、github.com/99designs/gqlgenをインポートする。

tools.go
package tools

import (
	_ "github.com/99designs/gqlgen"
)

以下のコマンドを実行。

$ go mod tidy

go.modファイルに記載されたパッケージ(github.com/99designs/gqlgen)とソースコードを見て、go mod tidy は go.mod ファイルがモジュール内のソースコードに合っていることを確認します。

さらに最新バージョンのgithub.com/99designs/gqlgenをインストール。
(多分ここは最新にこだわる必要はない。)

$ go get -d github.com/99designs/gqlgen@latest

スケルトンの作成

さらに以下コマンドで、プロジェクトのスケルトンを作成していきます。

$ go run github.com/99designs/gqlgen init

ここまで来るとドキュメント通り、以下のようなファイル構成が完成します。

├── go.mod
├── go.sum
├── gqlgen.yml               - The gqlgen config file, knobs for controlling the generated code.
├── graph
│   ├── generated            - A package that only contains the generated runtime
│   │   └── generated.go
│   ├── model                - A package for all your graph models, generated or otherwise
│   │   └── models_gen.go
│   ├── resolver.go          - The root graph resolver type. This file wont get regenerated
│   ├── schema.graphqls      - Some schema. You can split the schema into as many graphql files as you like
│   └── schema.resolvers.go  - the resolver implementation for schema.graphql
└── server.go                - The entry point to your app. Customize it however you see fit

main パッケージの main 関数が記述された server.goがエントリーポイントとなり、
ここでサーバーが立ち上がる。

server.go
package main

import (
	"go-project/graph"
	"log"
	"net/http"
	"os"

	"github.com/99designs/gqlgen/graphql/handler"
	"github.com/99designs/gqlgen/graphql/playground"
)

const defaultPort = "8080"

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = defaultPort
	}

	srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))

	http.Handle("/", playground.Handler("GraphQL playground", "/query"))
	http.Handle("/query", srv)

	log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

schema.graphqlsファイルを見てみると以下のような形になっていると思います。
後にGraphQL サーバーから実行するであろう QueryMutationの型定義が記述されていました。(Todo型、User型とあるので、Todoアプリの実装が可能なよう・・)

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!
}

リゾルバーの実装

後の工程も基本的にはドキュメントに沿ってやっていきます。
rosolver.goファイルは大本のルート定義をしていくためのファイルで、
以下のように追記します。

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

次に、schema.resolvers.goファイルに追記します。
このリゾルバーファイルは、先ほど見たスキーマの型定義ファイルやデータベースなどに定義したデータそのものを返す関数の定義をおこなうようです。

randパッケージのインポート元はmath/randを使用します。

schema.resolvers.go
// CreateTodo is the resolver for the createTodo field.
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
	todo := &model.Todo{
		Text: input.Text,
		ID:   fmt.Sprintf("T%d", rand.Int()),
		User: &model.User{ID: input.UserID, Name: "user " + input.UserID},
	}
	r.todos = append(r.todos, todo)
	return todo, nil
}

// Todos is the resolver for the todos field.
func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
	return r.todos, nil
}

GraphQLサーバーの起動

ここまでできると、あとは実際に GraphQL サーバーを起動します。

$ go run server.go

Query と Mutation実行してみる

ブラウザーでhttp://localhost:8080を開きます。
すると以下の画像のような画面が起動
http://localhost:8080

新しいタブを開いて、mutation を実行します。
※ mutation は RESTAPI でいうところの、POST、PATCH、DELETE を表現します。

mutation createTodo {
  createTodo(input: { text: "ゴミ捨て", userId: "1" }) {
    id
    text
    done
  }
}

実行すると右側に実行の応答が返ってきます。
schema.resolvers.goの CreateTodo 関数が実行される。
http://localhost:8080

そして、query を実行して取得します。
※query は RESTAPI でいうところの、GET を表現します。

query findTodos {
  todos {
    text
    done
    user {
      name
    }
  }
}

実行すると右側に応答が返ります。
http://localhost:8080

これで query、mutation の実行検証が済みました。

おわりに

この記事ではドキュメントに沿って Todo の実行をしましたが、
gqlgen を使ってGraphQL サーバーを動かしてみたいと思います!

コラボスタイル Developers

Discussion