什么是gqlgen?
gqlgen是一个go语言封装的一个graphql包,使用go语言对外暴露graphql接口
优点:gqlgen只依赖graphql schema。你只需要写好graphql schema就行,通过命令生成相关代码,包括接口、请求参数结构、返回参数结构,你只需要实现接口即可。
开始使用gqlgen
新建一个项目
make gqlgen-todos
初始化go mod
go mod init
go mod tidy
生成基础代码
go run github.com/99designs/gqlgen init
注意:刚开始可能因为没有地方引用gqlgen而导致本地没有github.com/99designs/gqlgen,可以先新建一个空文件,引入gqlgen
import (
_ "github.com/99designs/gqlgen"
)
然后go mod就可以引入gqlgen包了。
.
├── 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
可以看到生成了上述结构的代码。下面来详细分析一下各个文件的作用。
gqlgen.yml
gqlgen的配置文件,gqlgen命令生成代码的时候会用到,可以指定schema路径、代码生成路径等
graph/schema.graphqls
这个文件是graphql schema文件,gqlgen给出我们生成的,这就是我们写schema的地方,其他代码都是根据它生成的。我们也可以自己在里面写自己的schema并用go run github.com/99designs/gqlgen命令重新生成代码,我们也可以自己新建一个schema,并在gqlgen.yml文件中指定它的位置
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
schema:
- graph/*.graphqls
graph/generated/generated.go
运行时会调用的代码,包括接口定义,以及外部请求进来后先进入这个文件,然后对请求进行分发
graph/model/models_gen.go
自动生成的、schema中定义的type,input等所有的结构体
graph/schema.resolve.go
请求的入口,会对每个query,mutation生成对应的入口函数
graph/resolve.go
mutationResolver和queryResolver会继承它
type Resolver struct {
}
mutation和query公共的操作可以定义在Resolve上。
service.go
这个是main函数,开启http端口开始监听
自己写一个demo
为了更加方便体验gqlgen,避免不知道它在生成代码的时候做了什么,我们不要用它自带的schema文件,自己重新定义,并且更换位置,从头到尾体验一下。
.
├── go.mod
├── go.sum
├── gqlgen.yml
├── graph
│ ├── generated
│ │ └── generated.go
│ ├── model
│ │ └── models_gen.go
│ ├── mutation.resolvers.go
│ ├── query.resolvers.go
│ └── resolver.go
├── schema
│ ├── mutation.graphqls
│ ├── query.graphqls
│ ├── student.graphqls
│ └── teacher.graphqls
└── server.go
定义schema
在根目录下新建一个schema文件夹,定义了几个schema文件
schema/student.graphqls
type Student {
id: ID!
name: String!
tel: String
is_deleted: Boolean!
}
input NewStudent {
name: String!
tel: String
}
input ModifyStudentName {
id: ID!
name: String!
}
input DeleteStudent {
id: ID!
}
schema/teacher.graphqls
type Teacher {
id: ID!
name: String!
students: [Student!]!
}
input NewTeacher {
name: String!
student_ids: [ID!]!
}
schema/query.graphqls
type Query {
students: [Student!]!
teachers: [Teacher!]!
}
schema/mutation.graphqls
type Mutation {
createStudent(input: NewStudent!): Student!
modifyStudent(input: ModifyStudentName!): Boolean!
deleteStudent(input: DeleteStudent!): Boolean!
createTeacher(input: NewTeacher!): Teacher!
}
生成代码
go run github.com/99designs/gqlgen
改query
graph/query.resolvers.go
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"
"gqlgen-todos/graph/generated"
"gqlgen-todos/graph/model"
)
func (r *queryResolver) Students(ctx context.Context) ([]*model.Student, error) {
return r.students, nil
}
func (r *queryResolver) Teachers(ctx context.Context) ([]*model.Teacher, error) {
return r.teachers, nil
}
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type queryResolver struct{ *Resolver }
改mutation
graph/mutation.resolvers.go
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"
"gqlgen-todos/graph/generated"
"gqlgen-todos/graph/model"
"math/rand"
)
func (r *mutationResolver) CreateStudent(ctx context.Context, input model.NewStudent) (*model.Student, error) {
s := &model.Student{
ID: fmt.Sprintf("%s%d", input.Name, rand.Int()),
Name: input.Name,
Tel: input.Tel,
}
r.students = append(r.students, s)
return s, nil
}
func (r *mutationResolver) ModifyStudent(ctx context.Context, input model.ModifyStudentName) (bool, error) {
for i := range r.students {
if r.students[i].ID == input.ID {
r.students[i].Name = input.Name
return true, nil
}
}
return false, nil
}
func (r *mutationResolver) DeleteStudent(ctx context.Context, input model.DeleteStudent) (bool, error) {
for i := range r.students {
if r.students[i].ID == input.ID {
r.students[i].IsDeleted = true
return true, nil
}
}
return false, nil
}
func (r *mutationResolver) CreateTeacher(ctx context.Context, input model.NewTeacher) (*model.Teacher, error) {
t := &model.Teacher{
ID: fmt.Sprintf("%s%d", input.Name, rand.Int()),
Name: input.Name,
}
for i := range r.students {
for j := range input.StudentIds {
if input.StudentIds[j] == r.students[i].ID {
t.Students = append(t.Students, r.students[i])
}
}
}
return t, nil
}
// Mutation returns generated.MutationResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
type mutationResolver struct{ *Resolver }
运行
go run server.go
测试
创建学生
修改学生
modifyStudent
删除学生
查询学生
创建老师
先创建2个学生
然后创建老师
查询老师
源代码
总结
这篇文章主要介绍了一下go使用graphql,利用gqlgen包,参考文件[3]中有关于graphql的语法可以学习一下。
参考
[1]getting-started
[2]gqlgen
[3]graphql学习
[4]graphql和grpc之间的转换
[5]gqlgen_demo