Go × GraphQL でサーバーを作ってみた
はじめに
Go言語と初挑戦のGraphQL を使って、GraphQL サーバーを作ってみたいと思います。
▼Go言語公式ドキュメント
▼GraphQL公式ドキュメント
Goライブラリのgqlgenを使っていく
Go x GraphQL を両方学べるライブラリがありました。
その名もgqlgen(読み方は全くわからない・・)
公式ドキュメントにはこのように書いてありました。
(・_・D フムフム良くわからん。スキーマファーストってなんや。
gqlgenは、GraphQLサーバーを簡単に構築するためのGoライブラリです。
gqlgenはスキーマファーストのアプローチに基づいています
とにかくよくわからないので、手を動かしてみます。
以下のgqlgen公式ドキュメントに沿って環境を準備します。
環境構築
環境構築はドキュメントを参照しつつ、進めます。
まずは、 Go のプロジェクトを作成します。
$ mkdir go-project
$ cd go-project
$ go mod init go-project
これで Go プロジェクトが出来ました。
プロジェクト内には、go.mod
ファイルができています。
tools.go
ファイルを作成し、github.com/99designs/gqlgen
をインポートする。
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
がエントリーポイントとなり、
ここでサーバーが立ち上がる。
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 サーバーから実行するであろう Query
やMutation
の型定義が記述されていました。(Todo型、User型とあるので、Todoアプリの実装が可能なよう・・)
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
ファイルは大本のルート定義をしていくためのファイルで、
以下のように追記します。
type Resolver struct{
todos []*model.Todo
}
次に、schema.resolvers.go
ファイルに追記します。
このリゾルバーファイルは、先ほど見たスキーマの型定義ファイルやデータベースなどに定義したデータそのものを返す関数の定義をおこなうようです。
※rand
パッケージのインポート元はmath/rand
を使用します。
// 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
を開きます。
すると以下の画像のような画面が起動
新しいタブを開いて、mutation を実行します。
※ mutation は RESTAPI でいうところの、POST、PATCH、DELETE を表現します。
mutation createTodo {
createTodo(input: { text: "ゴミ捨て", userId: "1" }) {
id
text
done
}
}
実行すると右側に実行の応答が返ってきます。
schema.resolvers.go
の CreateTodo 関数が実行される。
そして、query を実行して取得します。
※query は RESTAPI でいうところの、GET を表現します。
query findTodos {
todos {
text
done
user {
name
}
}
}
実行すると右側に応答が返ります。
これで query、mutation の実行検証が済みました。
おわりに
この記事ではドキュメントに沿って Todo の実行をしましたが、
gqlgen を使ってGraphQL サーバーを動かしてみたいと思います!
Discussion