この記事は こちら をなぞってみた記事になります。
GraphQLをこれから始めたいという方におすすめ
gqlgenを利用してGraphQLサーバを実装する方法です。
- todoのリストを返却する
- 新しいtodoを作成する
- 完了したtodoをマークする
ちなみにココからの手順はgo moduleを利用した方法になります。
プロジェクトの作成
プロジェクトのディレクトリを作成し、go moduleを初期化します
$ mkdir gqlgen-todos $ cd gqlgen-todos $ go mod init # 必要に応じてパスを指定
サーバを構築します
スキーマ定義します
qglgenはスキーマファーストのライブラリです。 コードを書く前に、GraphQLスキーマ定義言語を使用してAPIを記述します。 以下のようなschema.graphqlというファイルを作成し、initコマンドによってコードを生成します。
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! }
プロジェクトスケルトンを作成する
以下のコマンドを実行します
$ go run github.com/99designs/gqlgen init
コマンドを実行すると
- gqlgen.yml
- generated.go
- models_gen.go
- resolver.go
- server/server.go
のスケルトンが作成されます
データベースモデルの作成
Todo用に生成されたモデルは正しくありません。 ユーザが埋め込まれていますが、要求されたときのみ取得できるようにします。 そのため、代わりにtodo.goで新しいモデルを作成します。
package gqlgen_todos type Todo struct { ID string Text string Done bool UserID string }
次に gqlgen.yml に以下を追加し、gqlgen を実行します。
models: Todo: model: github.com/[username]/gqlgen-todos.Todo
以下を実行し、再生成します
$ go run github.com/99designs/gqlgen -v
Note: -v フラグはgqlgenの動作を見るためにあります。
resolverを実装
ジェネレータが必要なresolverのインタフェースを生成しているので generated.go を確認します。
func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {} // ... } type Config struct { Resolvers ResolverRoot // ... } type ResolverRoot interface { Mutation() MutationResolver Query() QueryResolver Todo() TodoResolver } type MutationResolver interface { CreateTodo(ctx context.Context, input NewTodo) (*Todo, error) } type QueryResolver interface { Todos(ctx context.Context) ([]Todo, error) } type TodoResolver interface { User(ctx context.Context, obj *Todo) (*User, error) }
~~
resolverを実装する
既存のコードを修正することができないため、 resolver.go
を削除した後、 gqlgen を実行することで強制的に再実行します。
$ rm resolver.go
$ go run github.com/99designs/gqlgen
resolver.go の実装されていないところを埋めていきます。
package gqlgen_todos import ( context "context" "fmt" "math/rand" ) type Resolver struct { todos []*Todo } func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } func (r *Resolver) Todo() TodoResolver { return &todoResolver{r} } type mutationResolver struct{ *Resolver } func (r *mutationResolver) CreateTodo(ctx context.Context, input NewTodo) (*Todo, error) { todo := &Todo{ Text: input.Text, ID: fmt.Sprintf("T%d", rand.Int()), UserID: input.UserID, } r.todos = append(r.todos, todo) return todo, nil } type queryResolver struct{ *Resolver } func (r *queryResolver) Todos(ctx context.Context) ([]*Todo, error) { return r.todos, nil } type todoResolver struct{ *Resolver } func (r *todoResolver) User(ctx context.Context, obj *Todo) (*User, error) { return &User{ID: obj.UserID, Name: "user " + obj.UserID}, nil }
Resolver, CreateTodo, Todos, User 内を実装しています。
動作するようになったのでサーバを起動してみましょう
$ go run server/server.go
起動が成功したら、ブラウザで http://localhost:8080
へアクセスし、
以下のクエリを試してみましょう
これがmutationなので、todoを作成するクエリです
mutation createTodo { createTodo(input:{text:"todo", userId:"1"}) { user { id } text done } }
以下のクエリで作成したtodoを取得できます
query findTodos { todos { text done user { name } } }
仕上げ
resolver.go のpackageとimportの間に以下の行を追加します。
//go:generate go run github.com/99designs/gqlgen
このマジックコメントは、コードを再生成したいときに実行するコマンドになります。
プロジェクト全体で再帰的に生成を実行するには、以下のコマンドを使用します。
$ go generate ./...
いい感じですね!