我尝试创建了一个用Go编写的支持GraphQL的服务器
由於我在 Go 中嘗試了 GraphQL 的伺服器端實現,所以在這裡留下了一篇筆記。
准备
我們試著使用 vektah/gqlgen 這個函式庫來快速建立一個嚴格型別的 GraphQL 伺服器。
$ go get -u github.com/vektah/gqlgen
实施
首先,在项目目录下创建./schema.graphql文件。
该文件将包括User类型定义、Query/Mutation定义以及输入参数定义。
type User {
id: ID!
name: String!
}
type Query {
user(id: String!): User
users: [User!]!
}
input NewUser {
name: String!
}
type Mutation {
createUser(input: NewUser!): User!
}
接下来,创建一个名为graph的目录,并创建以下两个文件。
package graph
type User struct {
ID string
Name string
}
{
"User": "github.com/s-ichikawa/gql-todo/graph.User"
}
既经过此步骤定义了架构,请执行以下命令。
$ cd path/to/project/graph
$ gqlgen -typemap type.json -schema ../schema.graphql
生成的.go和model_get.go文件将出现在graph目录中。这些文件是根据架构定义生成的GraphQL服务器的代码。由于代码很长,我将省略其内容,但重要的是generated.go文件的Resolver接口。
type Resolvers interface {
Mutation_createUser(ctx context.Context, input NewUser) (User, error)
Query_user(ctx context.Context, id string) (*User, error)
Query_users(ctx context.Context) ([]User, error)
}
接下来将实施这一计划。
package graph
import (
"context"
"fmt"
"math/rand"
"github.com/pkg/errors"
)
type User struct {
ID string
Name string
}
type Resolver struct {
users []User
}
func (r *Resolver) Mutation_createUser(ctx context.Context, input NewUser) (User, error) {
user := User{
ID: fmt.Sprintf("%d", rand.Int()),
Name: input.Name,
}
r.users = append(r.users, user)
return user, nil
}
func (r *Resolver) Query_user(ctx context.Context, id string) (*User, error) {
for _, user := range r.users {
if (user.ID == id) {
return &user, nil
}
}
return &User{}, errors.New("Sorry, Not Found.")
}
func (r *Resolver) Query_users(ctx context.Context) ([]User, error) {
return r.users, nil
}
接下来创建main.go
/通过该路径可以打开一个可以执行Playground查询的页面的端点
/query是接收GraphQL的端点
package main
import (
"github.com/s-ichikawa/gql-todo/graph"
"net/http"
"github.com/vektah/gqlgen/handler"
"fmt"
"log"
)
func main() {
resolvers := &graph.Resolver{}
http.Handle("/", handler.Playground("Todo", "/query"))
http.Handle("/query", handler.GraphQL(graph.MakeExecutableSchema(resolvers)))
fmt.Println("Listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
尝试运行一下
$ go run main.go
Listening on :8080





我能够以非常简单的方式实现一个能够使用GraphQL的服务器。如果将用户的保存和获取从数据库中进行,那么graph.go文件中的r.users = append(r.users, user)和for _, user := range r.users的部分将被替换为SQL查询。
这次暂时不处理DB等内容,而是稍微玩一下模式定义,试试看能否注册与用户相关的待办事项。
定义用于Todo的类型和Query/Mutation。
+ type Todo {
+ id: ID!
+ text: String!
+ done: Boolean!
+ user: User!
+ }
type Query {
user(id: String!): User
users: [User!]!
+ todo(id: String!): Todo!
+ todos: [Todo!]!
}
+ input NewTodo {
+ text: String!
+ userId: String!
+ }
type Mutation {
createUser(input: NewUser!): User!
+ createTodo(input: NewTodo!): Todo!
}
在type.json中添加Todo的定义
{
"User": "github.com/s-ichikawa/gql-todo/graph.User",
"Todo": "github.com/s-ichikawa/gql-todo/graph.Todo"
}
在graph.go中添加Todo类型。
type Todo struct {
ID string
Text string
UserId string
}
请再次运行 gqlgen 命令
$ cd path/to/project/graph
$ gqlgen -typemap type.json -schema ../schema.graphql
实现了一个添加的 Resolver(由于很长,所以只记录添加的部分)
与 User 相比几乎没有变化,但添加了一个解决 Schema 定义中的 `try Todo {…, user: User!}` 的 `func Todo_user`。
func (r *Resolver) Mutation_createTodo(ctx context.Context, input NewTodo) (Todo, error) {
todo := Todo{
ID: fmt.Sprintf("T%d", rand.Int()),
UserId: fmt.Sprintf("U%d", input.UserId),
Text: input.Text,
}
r.todos = append(r.todos, todo)
return todo, nil
}
func (r *Resolver) Query_todo(ctx context.Context, id string) (Todo, error) {
for _, todo := range r.todos {
if todo.ID == id {
return todo, nil
}
}
return Todo{}, errors.New("Not Found")
}
func (r *Resolver) Query_todos(ctx context.Context) ([]Todo, error) {
return r.todos, nil
}
func (r *Resolver) Todo_user(ctx context.Context, obj *Todo) (User, error) {
for _, user := range r.users {
if (user.ID == obj.UserId) {
return user, nil
}
}
return User{}, errors.New("Sorry, Not Found.")
}
重新启动 main.go 并尝试重新发出查询。

已成功获取到Todo以及其相关联的用户信息。


所以,因为成功获取了与某个类型相关的另一个类型,所以先到此为止。
添加附注
不过顺便说一下,当我运行 main.go 的时候出现了以下这样的错误:
$ go get github.com/gorilla/websocket,所以我还是记下来。
$ go run main.go
../../vektah/gqlgen/handler/graphql.go:10:2: cannot find package "github.com/gorilla/websocket" in any of:
...