Lambdaカクテル

京都在住Webエンジニアの日記です

gqlgenの練習してGraphQL入門した

仕事でGraphQLを使う機会があり,言語はGoなので,gqlgenの練習をした。

github.com

gqlgen なに

GraphQLのSchemaを与えることでGraphQLのサーバのGoコードを生成してくれるジェネレータツール。

やったこと

チュートリアルをなぞっただけ。

gqlgen.com

環境構築

Makefileにいろいろ書いたりするいつもの作業。go modの理解がまだ十分でないので,go mod init github.com/windymelt/go-gqlgen-exerciseみたいなのをいきなり入力させられるのに戸惑う。公開前提の世界観なのだろうか。

gqlgen入れる(Schema書く)

入れるといってもgo runすれば勝手に降ってくるので,まずスキーマを書く。schema.graphqlを書く。

type Sixsixsix {
  id: ID!
  text: String!
}

type Query {
  ssss: [Sixsixsix!]!
}

input NewSixsixsix {
  id: ID!
  text: String!
}

type Mutation {
  create666(input: NewSixsixsix!): Sixsixsix!
}

どうやらtype Querytype Mutaionは必須らしい,ということがこのへんで分かってくる。実装しながら覚えていくタイプ。 ちゃんと勉強しろという説もあるが,刺激がないと覚えられないタイプなのでこのへんは難しいですね。

で,gqlgenを走らせるといろいろ生成される。

go run github.com/99designs/gqlgen -v
windymelt% tree  
.
├── Makefile
├── generated.go
├── go.mod
├── go.sum
├── gqlgen.yml
├── main.go
├── models_gen.go
├── resolver.go
└── schema.graphql

自分は事前にmain.goを書いていたのでこういうディレクトリ構造になった。なにか色々と生成されているが,生成されたコードまわりは分けたい。goのパッケージまわりとか勉強したらちゃんと分けられるようになりそう。

main.go書く1

ひとまず動いた感を出したい。gqlgenはplaygroundsというブラウザ上でGraphQLを叩けるやつも提供しているので,ひとまずこれを動かす。

// main.go
package main

import (
    "log"
    "net/http"

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

func main() {
    http.Handle("/", handler.Playground("GraphQL Playground", "/query"))

    log.Print("listening on http://localhost:8000")
    log.Fatal(http.ListenAndServe(":8000", nil))
}

これでビルドして動かすとlocalhost:8000で以下のような画面が表示できるようになる。

f:id:Windymelt:20191219175934p:plain
localhost:8000

resolver実装する

このままだと何も実装されていないのでクエリを叩いてもサーバは死んでしまう。実際に飛んできたクエリをいい感じに処理して(表現が雑すぎる)データを返してくれる箇所を実装する必要があって,これをresolverという。

gqlgenは適当に(panicとかで)穴が開いたresolverをresolver.goに吐出するので,ここを実装して穴埋めをすればよい。

package main

import (
    "context"
    "fmt"
    "math/rand"
) // THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES.

type Resolver struct{
    ssss []*Sixsixsix
}

func (r *Resolver) Mutation() MutationResolver {
    return &mutationResolver{r}
}
func (r *Resolver) Query() QueryResolver {
    return &queryResolver{r}
}

type mutationResolver struct{ *Resolver }

// ↓ここ実装した
func (r *mutationResolver) Create666(ctx context.Context, input NewSixsixsix) (*Sixsixsix, error) {
    s := &Sixsixsix{
        Text:   input.Text,
        ID:     fmt.Sprintf("T%d", rand.Int()),
    }
    r.ssss = append(r.ssss, s)
    return s, nil
}

type queryResolver struct{ *Resolver }

// ↓ここ実装した
func (r *queryResolver) Ssss(ctx context.Context) ([]*Sixsixsix, error) {
    return r.ssss, nil
}

ほぼチュートリアルのコピペだが,なんとなく雰囲気はわかる。なんらかのコンテキストが渡ってくるので,それを元にデータを引いてきたり,追加したりすればよいらしい。

とはいえ今回は適当にオンメモリでデータを差し込んだり引いたりするので,コンテキストを読む必要がない。適当にResolverのフィールドに値を突っ込む(なんて無作法な!と思ったあなた,これはチュートリアルにもそう書いてある。) やっていることはといえば,渡ってきたtextフィールドとランダムに生成したidを使ってデータを追加したり,contextを完全無視して配列の中身を全部返したりしている。

main.go書く2

以下の行をいい感じに追加する。

http.Handle("/query", handler.GraphQL(NewExecutableSchema(Config{Resolvers: &Resolver{}})))

するとデータを作成できるようになる。データ作成もMutationの範疇に入るらしい。

f:id:Windymelt:20191219180659p:plain
Mutation

データをQueryすることもできるようになった。

f:id:Windymelt:20191219180814p:plain
Query

とはいえパラメータをつけてQueryとかはできない。あとはコードをいじっていったら色々なQueryやMutationができるようになりそう。

結語

  • gqlgenのさわりだけ練習し,基礎を学ぶことができました。GraphQLについてもっと勉強して,より基本的な実装に進めるようになりたいですね。
  • Go modulesの仕組みやメンタルモデルはまだうまく学べていないので,これからの課題としたいですね。

文献

github.com