在Go语言中学习GraphQL服务器端(2)-尝试运行GraphQL服务器
你好。
第二部分介绍如何“运行GraphQL服务器”。
关于这一章
让我们立即来看一下GraphQL服务器端代码。
这次我们将使用一个名为gqlgen的Go语言GraphQL服务器端库。
gqlgen命令具有生成TODO管理API代码的示例应用的功能,因此我们将生成这个代码并将其实际运行起来。
gqlgen 命令
gqlgen命令是一个用于从GraphQL模式信息自动生成使用Go编写的服务器端代码的命令。
安装
由于gqlgen命令是使用Go语言编写的命令,因此可以使用go install命令进行安装。
$ go install github.com/99designs/gqlgen@latest
$ gqlgen version
v0.17.22
生成样本代码
在中文中,可以这样表述:
生成TODO管理API的样例应用程序代码的命令是gqlgen init。
在为放置样例代码而准备的目录中执行go mod init后,尝试执行gqlgen init。
$ go mod init my_gql_server
$ go get -u github.com/99designs/gqlgen
$ gqlgen init
Creating gqlgen.yml
Creating graph/schema.graphqls
Creating server.go
Generating...
Exec "go run ./server.go" to start GraphQL server
自动生成代码的说明
从这里开始,我们将解释由gqlgen init生成的代码。
目录结构
以下是生成的代码列表。
.
├─ graph
│ ├─ generated.go # リゾルバをサーバーで稼働させるためのコアロジック部分
│ ├─ model
│ │ └─ models_gen.go # GraphQLのスキーマオブジェクトがGoの構造体として定義される
│ ├─ resolver.go # ルートリゾルバ構造体の定義
│ ├─ schema.graphqls # GraphQLスキーマ定義
│ └─ schema.resolvers.go # ビジネスロジックを実装するリゾルバコードが配置
├─ gqlgen.yml # gqlgenの設定ファイル
├─ server.go # サーバーエントリポイント
├─ go.mod
└─ go.sum
图/模式.graphqls – GraphQL模式定义
GraphQL存在着一个被称为模式的概念。
-
- どのようなオブジェクト型が用意されているのか
- どのようなクエリ・ミューテーションがあるのか
我会将这些信息写入一个拓展名为.graphqls的文件中。
这里放置的是使用gqlgen init命令生成的示例应用程序代码,用于管理TODO事项。所以,这里放置了用于TODO管理的模式。
生成的模式内容
# GraphQL schema example
#
# https://gqlgen.com/getting-started/
type Todo {
id: ID!
text: String!
done: Boolean!
user: User!
}
type User {
id: ID!
name: String!
}
type Query {
todos: [Todo!]!
}
input NewTodo {
text: String!
userId: String!
}
type Mutation {
createTodo(input: NewTodo!): Todo!
}
graph/model/models_gen.go中定义了对象结构体。
以下の機能があります。
– タスクの追加と削除
– 期限の設定
– タスクの優先順位の設定
– タスクの分類とタグの追加
– 完了したタスクのチェックマークの表示
-
- 追加されたTODO
- TODOタスクのオーナーとなるユーザー
有两种类型的对象存在,每种对象都在graph/schema.graphqls中定义了自己的模式。
在models_gen.go文件中,定义了与这个TODO对象和用户对象对应的Go结构体类型。
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"`
}
图形/解析器.go·图形/模式.resolvers.go – 解析器代码
这里生成了服务器端GraphQL的核心部分,称为”解析器”的模板。在resolver.go中,定义了一个名为Resolver的解析器结构类型;在schema.resolvers.go中定义了一些方法。
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct{}
type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
// CreateTodo is the resolver for the createTodo field.
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
panic(fmt.Errorf("not implemented: CreateTodo - createTodo"))
}
// Todos is the resolver for the todos field.
func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
panic(fmt.Errorf("not implemented: Todos - todos"))
}
以下是GraphQL模式中定义的查询和变更,以及生成的解析器方法之间的对应关系。
-
- ミューテーションcreateTodo(input: NewTodo!): Todo!が呼ばれたときにCreateTodoメソッドが呼ばれる
- クエリtodos: [Todo!]!が呼ばれたときにはTodosメソッドが呼ばれる
在GraphQL的模式中定义的变异和查询是再次引用的。
type Mutation {
createTodo(input: NewTodo!): Todo!
}
type Query {
todos: [Todo!]!
}
在服务器端实现GraphQL的过程中,需要重写这一部分来创建业务逻辑,并在接收请求时包含panic。
// ミューテーションcreateTodoが呼ばれた際に実行されるコード
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
// TODO:
// ユーザーから受け取ったリクエスト情報inputを使ってTODOを登録し、
// その登録されたTODOの情報をmodel.TODO型の戻り値に入れて返却
}
// クエリtodosが呼ばれた際に実行されるコード
func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
// TODO:
// レスポンスに含めるTODO一覧を、戻り値[]*model.Todoに入れて返却
}
graph/generated.go- 主要逻辑部分用于在服务器上运行解析器。
從開發者的角度來看,只需填寫在schema.resolvers.go中生成的解析方法的內容即可,但本質上講
-
- GraphQLのリクエストがサーバーに届く
-
- リクエストに含まれているクエリを解釈し「呼ばれているのはスキーマに定義されたtodosクエリだ」と判断する
- リゾルバ構造体のTodosメソッドを呼ぶ
需要一系列的判断逻辑。
在todos查询的情况下,开发者编写的解析方法的返回值为[]model.Todo类型。然而,为了使API服务器正常运行,生成将该[]model.Todo类型转化为用户返回的JSON响应体的部分也是必不可少的逻辑。
这样
-
- ユーザーから受け取ったHTTPリクエストボディに含まれているクエリから、適切なリゾルバを呼び出す
- リゾルバが返した結果をHTTPレスポンスに変換して、ユーザーに返却する
生成的代码generated.go实现了作为HTTP服务器运行的解析器的桥接部分。
生成的代码存在于generated.go文件中,是由gqlgen根据GraphQL模式自动生成的。因此,开发者不需要手动编辑或修改这个文件的内容。
server.go – 服务器入口点
这是GraphQL服务器的入口点部分。
-
- デフォルトでは8080番ポートで稼働し、
/queryにGraphQLリクエストを送ったら結果が返ってきて、
/をブラウザで開くとクエリを実行するためのPlaygroundが使える
已实施了这项功能。
生成的入口点的内容
server.go
package main
import (
"log"
"my_gql_server/graph"
"net/http"
"os"
"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(graph.NewExecutableSchema(graph.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
gqlgen.yml是一个用于编写在使用gqlgen命令生成代码时的配置的yaml文件。
例如,可以配置以下项目。
-
- スキーマに定義されたオブジェクトをGoの構造体にしたものはどこに置くのか(デフォルトでgraph/model直下のmodelパッケージ)
generated.goをどこに生成するのか(デフォルトでgraph直下)
GraphQLのスキーマファイルの置き場所(デフォルトでgraph直下にある拡張子.graphqlsファイルを使用)
生成的 gqlgen.yml 文件内容(摘录)
# 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.go
package: graphs
# Where should any generated models go?
model:
filename: graph/model/models_gen.go
package: model
试着运行生成的样例TODO管理API服务器
现在我们已经介绍了生成的代码,那就让我们立刻来运行它吧。
填写解析器的内容
首先,让我们填充在graph/schema.resolvers.go中生成的解析器的内容。
理论上应该连接到数据库并执行数据的插入和查询语句,但由于我们只想确认操作是否正常,所以我们将只返回一个适当的TODO结构。
// CreateTodo is the resolver for the createTodo field.
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
- panic(fmt.Errorf("not implemented: CreateTodo - createTodo"))
+ return &model.Todo{
+ ID: "TODO-3",
+ Text: input.Text,
+ User: &model.User{
+ ID: input.UserID,
+ Name: "name",
+ },
+ }, nil
}
// Todos is the resolver for the todos field.
func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
- panic(fmt.Errorf("not implemented: Todos - todos"))
+ return []*model.Todo{
+ {
+ ID: "TODO-1",
+ Text: "My Todo 1",
+ User: &model.User{
+ ID: "User-1",
+ Name: "テスト",
+ },
+ Done: true,
+ },
+ {
+ ID: "TODO-2",
+ Text: "My Todo 2",
+ User: &model.User{
+ ID: "User-1",
+ Name: "テスト",
+ },
+ Done: false,
+ },
+ }, nil
}
启动服务器
通过运行server.go作为入口点,可以启动GraphQL服务器。默认情况下,会使用8080端口。
$ go run ./server.go
2023/10/17 01:07:34 connect to http://localhost:8080/ for GraphQL playground
在Playground上执行查询
当在浏览器中打开http://localhost:8080/时,您可以访问以下的Playground来执行查询。
执行Todos查询
我尝试执行todos查询。
query {
todos {
id
text
done
user {
name
}
}
}
經過驗證,可以確認在解析器內靜態地返回的TODO結構體內容已經被獲取。
执行createTodo变更。
接下来,让我们尝试执行 createTodo 变异。
mutation {
createTodo(input: {
text: "test-create-todo"
userId: "test-user-id"
}){
id
text
done
user {
id
name
}
}
}
这里也显示了通过解析器返回的内容。经过测试,操作成功。
接下来的部分 (Cì de
我这次尝试了运行gqlgen提供的示例应用程序,但从下次开始,我想用自己的内容来创建GraphQL服务器。
就是这样。
谢谢。
请多关照。