使用gqlgen搭建graphql的go服務端

添加依賴

require (
	github.com/99designs/gqlgen v0.11.3
	github.com/vektah/gqlparser/v2 v2.0.1
)

編寫schema

在項目根目錄創建文件夾,graph,在graph文件夾中新建schema.graphqls文件,在其中編寫schema定義,比如

type Query {
    hello: String
}

生成go代碼

在項目根目錄新建go源代碼,內容爲

package main

import "github.com/99designs/gqlgen/cmd"

func main() {
	cmd.Execute()
}

接下來,使用命令生成相關go代碼

go run 源代碼文件名.go init

此時命令行會輸出

validation failed: packages.Load: -: package test/graph/model is not in GOROOT ...

這是因爲我們沒有在graph文件夾中,創建graph的modal文件夾及源文件(因爲我們沒有定義其他type,不需要用到,不影響)
此時執行後,目錄結構爲

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         2020/5/5     16:08                graph
-a----         2020/5/4     17:44            110 go.mod
-a----         2020/5/4     17:44           7599 go.sum
-a----         2020/5/4     17:15             94 gqlgen.go
-a----         2020/5/5     16:08           1667 gqlgen.yml
-a----         2020/5/5     16:08            635 server.go

其中的gqlgen.yml和server.go是自動生成的代碼,server.go就是運行這個graph服務端程序的go代碼,默認使用8080端口,直接將其作爲普通go程序運行則代表啓動服務端,這裏還不能啓動,因爲我們定義的schema裏的查詢還沒有編寫實現

package main

import (
	"log"
	"net/http"
	"os"
	"test/graph"
	"test/graph/generated"

	"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(generated.NewExecutableSchema(generated.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))
}

gqlgen.yml是默認的配置文件,配置瞭如何讀取graph文件夾下內容進行go代碼生成,因爲剛剛我們沒有編寫這個文件,所以會使用默認配置,內容如下

# Where are all the schema files located? globs are supported eg  src/**/*.graphqls
schema:
  - graph/*.graphqls

# Where should the generated server code go?
exec:
  filename: graph/generated/generated.go
  package: generated

# Uncomment to enable federation
# federation:
#   filename: graph/generated/federation.go
#   package: generated

# Where should any generated models go?
model:
  filename: graph/model/models_gen.go
  package: model

# Where should the resolver implementations go?
resolver:
  layout: follow-schema
  dir: graph
  package: graph

# Optional: turn on use `gqlgen:"fieldName"` tags in your models
# struct_tag: json

# Optional: turn on to use []Thing instead of []*Thing
# omit_slice_element_pointers: false

# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true

# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
autobind:
  - "test/graph/model"

# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
models:
  ID:
    model:
      - github.com/99designs/gqlgen/graphql.ID
      - github.com/99designs/gqlgen/graphql.Int
      - github.com/99designs/gqlgen/graphql.Int64
      - github.com/99designs/gqlgen/graphql.Int32
  Int:
    model:
      - github.com/99designs/gqlgen/graphql.Int
      - github.com/99designs/gqlgen/graphql.Int64
      - github.com/99designs/gqlgen/graphql.Int32

正是其中的以下代碼,指定了讀取這個路徑文件作爲modal文件,而我們沒有提供,在剛剛運行纔會有validate fail的提示

model:
  filename: graph/model/models_gen.go
  package: model

如果需要,我們也可以創建modal文件夾,在modal文件夾中編寫models_gen.go文件,如下,每個字段右側的 ``註釋代表標明json序列化時的鍵值名稱

package model

type NewTodo struct {
	Text   string `json:"text"`
	UserID string `json:"userId"`
}

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

type User struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

而在graph文件夾中,也生成了對應go代碼

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         2020/5/5     16:08                generated
-a----         2020/5/5     16:08            184 resolver.go
-a----         2020/5/5     11:35             32 schema.graphqls
-a----         2020/5/5     16:08            544 schema.resolvers.go

其中的generated文件夾中包含一個generated.go,是框架的處理邏輯封裝,一般由編譯生成不去修改,比如剛剛的hello,會被編譯爲一個接口

type QueryResolver interface {
	Hello(ctx context.Context) (*string, error)
}

我們唯一需要關注的是schema.resolvers.go這個文件,其中定義了每個查詢的返回結果,我們需要在其中用golang編寫查詢結果的實現

package graph

// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.

import (
	"context"
	"fmt"
	"test/graph/generated"
)

func (r *queryResolver) Hello(ctx context.Context) (*string, error) {
	panic(fmt.Errorf("not implemented"))
}

// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }

type queryResolver struct{ *Resolver }

像是這個,剛剛的hello在這裏被編譯爲一個go的函數,所以我們通過編寫這個函數的實現,決定返回給客戶端的內容,比如返回字符串helllo

package graph

import (
	"context"
	"test/graph/generated"
)

func (r *queryResolver) Hello(ctx context.Context) (*string, error) {
	retval := string("hello")
	return &retval,nil
}

func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }

type queryResolver struct{ *Resolver }

運行服務端

實現了查詢就可以啓動服務端了,在項目根目錄執行

go run .\server.go

會看到

connect to http://localhost:8080/ for GraphQL playground

此時訪問該地址,就可以看到服務端提供的web查詢頁面,在左邊的輸入框編寫查詢,點擊播放按鈕即可在右邊看到服務端返回的查詢結果
gqlgen的web服務端
歡迎找歪歪梯聊騷

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章