使用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
- 1
会看到
connect to http://localhost:8080/ for GraphQL playground
- 1
此时访问该地址,就可以看到服务端提供的web查询页面,在左边的输入框编写查询,点击播放按钮即可在右边看到服务端返回的查询结果
https://www.freesion.com/article/1315615190/