用Go学习GraphQL服务器端(3)- 使用自定义的模式创建GraphQL服务器
你好。
第三部分是关于“使用自定义模式创建GraphQL服务器”的内容。
关于这一章
在Part2中,我们运行了gqlgen的示例应用程序,但在实际开发场景中,我们将根据自定义的GraphQL模式创建服务器的内容。
因此,本次我们将介绍从一个具有非TODO应用程序的原始模式文件的状态开始,创建服务器端代码的步骤。
本次主题 – 简化版本的GitHub API v4
我认为在实际存在的GraphQL API中,最著名的应该是GitHub的API。
因此,这次我们的目标是模仿GitHub API v4的内容。
由于时间和篇幅的限制,很遗憾无法完整地实现GitHub API v4的所有内容。因此,本次我们只选择了一些关键点进行简化并进行演示。
这次再现的GraphQL模式
directive @isAuthenticated on FIELD_DEFINITION
scalar DateTime
scalar URI
interface Node {
id: ID!
}
type PageInfo {
endCursor: String
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
}
type Repository implements Node {
id: ID!
owner: User!
name: String!
createdAt: DateTime!
issue(
number: Int!
): Issue
issues(
after: String
before: String
first: Int
last: Int
): IssueConnection!
pullRequest(
number: Int!
): PullRequest
pullRequests(
after: String
before: String
first: Int
last: Int
): PullRequestConnection!
}
type User implements Node {
id: ID!
name: String!
projectV2(
number: Int!
): ProjectV2
projectV2s(
after: String
before: String
first: Int
last: Int
): ProjectV2Connection!
}
type Issue implements Node {
id: ID!
url: URI!
title: String!
closed: Boolean!
number: Int!
author: User!
repository: Repository!
projectItems(
after: String
before: String
first: Int
last: Int
): ProjectV2ItemConnection!
}
type IssueConnection {
edges: [IssueEdge]
nodes: [Issue]
pageInfo: PageInfo!
totalCount: Int!
}
type IssueEdge {
cursor: String!
node: Issue
}
type PullRequest implements Node {
id: ID!
baseRefName: String!
closed: Boolean!
headRefName: String!
url: URI!
number: Int!
repository: Repository!
projectItems(
after: String
before: String
first: Int
last: Int
): ProjectV2ItemConnection!
}
type PullRequestConnection {
edges: [PullRequestEdge]
nodes: [PullRequest]
pageInfo: PageInfo!
totalCount: Int!
}
type PullRequestEdge {
cursor: String!
node: PullRequest
}
type ProjectV2 implements Node {
id: ID!
title: String!
url: URI!
number: Int!
items(
after: String
before: String
first: Int
last: Int
): ProjectV2ItemConnection!
owner: User!
}
type ProjectV2Connection {
edges: [ProjectV2Edge]
nodes: [ProjectV2]
pageInfo: PageInfo!
totalCount: Int!
}
type ProjectV2Edge {
cursor: String!
node: ProjectV2
}
union ProjectV2ItemContent = Issue | PullRequest
type ProjectV2Item implements Node {
id: ID!
project: ProjectV2!
content: ProjectV2ItemContent
}
type ProjectV2ItemConnection {
edges: [ProjectV2ItemEdge]
nodes: [ProjectV2Item]
pageInfo: PageInfo!
totalCount: Int!
}
type ProjectV2ItemEdge {
cursor: String!
node: ProjectV2Item
}
type Query {
repository(
name: String!
owner: String!
): Repository
user(
name: String!
): User @isAuthenticated
node(
id: ID!
): Node
}
input AddProjectV2ItemByIdInput {
contentId: ID!
projectId: ID!
}
type AddProjectV2ItemByIdPayload {
item: ProjectV2Item
}
type Mutation {
addProjectV2ItemById(
input: AddProjectV2ItemByIdInput!
): AddProjectV2ItemByIdPayload
}
本次再现的GraphQL模式
这次准备的物品在这里。
-
- User: GitHubにアカウント登録しているユーザー
そのユーザーが作成したレポジトリ一覧が取得できる
Repository
レポジトリの所有者であるユーザーの情報が取得できる
レポジトリが持つIssue/PR一覧が取得できる
Issue/PR番号を指定することでそのIssue/PRの詳細が取得できる
Issue
Issue作成ユーザーの情報が取得できる
そのIssueが紐づいているレポジトリの情報が取得できる
そのIssueが紐づいているProjectV2Item一覧が取得できる
PullRequest
そのPRが紐づいているレポジトリの情報が取得できる
そのPRが紐づいているProjectV2のカードが取得できる
ProjectV2: IssueやPRをカードにしてかんばんを作ることができる実在の機能
そのかんばんの作者であるユーザーの情報が取得できる
かんばんのカード一覧が取得できる
ProjectV2Item: かんばんのカード
そのカードがどこのかんばんのものなのか情報を取得できる
そのカードの実態となっているIssueもしくはPRの情報を取得できる
这次要实现的查询/变更操作
我們需要在實際的GitHub API v4中進行各種操作,因此我們打算創建以下四個查詢和變異。
-
- クエリ
node: 各オブジェクト(=ノード)に割り当てられた一意のIDからオブジェクト情報を取得する
user: ユーザー名からユーザーの情報を取得する
repository: 作成者とレポジトリ名からレポジトリの情報を取得する
ミューテーション
addProjectV2ItemById: Issue/PRをかんばんのカードとして追加する
准备自己的GraphQL服务器仓库。
我已经准备好了架构,然后准备了一个开发仓库,并希望在那里生成与我自己的架构相符的服务器代码。
使用go mod init来准备Go项目
在创建一个新目录后执行`go mod init`命令,并将gqlgen通过`go get`命令放置在这个目录中,与前一章节的操作相同。
$ go mod init github.com/saki-engineering/graphql-sample
$ go get -u github.com/99designs/gqlgen
自定义gqlgen生成的目录结构。
既然如此,让我们尝试自定义gqlgen生成的代码结构,以符合个人喜好。
这次我们的最终目标是下面这样的结构。差异显示了与默认设置的区别。
.
+├─ internal
+│ └─ generated.go # このファイルの中身は編集しない
├─ graph
-│ ├─ generated.go # このファイルの中身は編集しない
│ ├─ model
│ │ └─ models_gen.go # 定義した型が構造体として定義される
│ ├─ resolver.go
-│ ├─ schema.graphqls # スキーマ定義
│ └─ schema.resolvers.go # この中に、各queryやmutationのビジネスロジックを書く
+├─ schema.graphqls # スキーマ定義
├─ gqlgen.yml # gqlgenの設定ファイル
├─ server.go # エントリポイント
├─ go.mod
└─ go.sum
为了生成这个结构,我们需要将gqlgen.yml的内容放置在存储库的根目录下,并设置如下内容。(差异是指与gqlgen init生成的默认文件的区别)
# 自動生成コードの元となるGraphQLスキーマがどこに配置してあるか
schema:
- - graph/*.graphqls
+ - ./*.graphqls
# 自動生成されるgeneated.goの置き場所
exec:
- filename: graph/generated.go
- package: graph
+ filename: internal/generated.go
+ package: internal
# スキーマオブジェクトに対応するGo構造体の置き場所
model:
filename: graph/model/models_gen.go
package: model
# リゾルバコードの置き場所
resolver:
layout: follow-schema
dir: graph
package: graph
使用gqlgen generate进行代码生成
目前目录的内容应该如下所示。
.
├─ schema.graphqls # 自作GraphQLスキーマ
├─ gqlgen.yml # gqlgenの設定ファイル
├─ go.mod
└─ go.sum
为了生成符合schema.graphqls模式的Go代码,我们执行gqlgen generate命令。
$ gqlgen generate
然后,generated.go和resolver code将在目录中生成,并处于以下状态。
.
+├─ internal
+│ └─ generated.go # このファイルの中身は編集しない
+├─ graph
+│ ├─ model
+│ │ └─ models_gen.go # 定義した型が構造体として定義される
+│ ├─ resolver.go
+│ └─ schema.resolvers.go # この中に、各queryやmutationのビジネスロジックを書く
├─ schema.graphqls # スキーマ定義
├─ gqlgen.yml # gqlgenの設定ファイル
├─ go.mod
└─ go.sum
服务器入口点的布置
在gqlgen generate命令中,与gqlgen init不同的是不会生成作为服务器入口点的server.go文件。
因此,需要参考通过gqlgen init生成的server.go文件,自己手动创建入口点。
服务器.go
import (
// (一部抜粋)
"github.com/saki-engineering/graphql-sample/graph"
+ "github.com/saki-engineering/graphql-sample/internal"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}
- srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))
+ srv := handler.NewDefaultServer(internal.NewExecutableSchema(internal.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))
}
现在,我们已经建立了使用自定义模式创建GraphQL服务器的框架。
下一个部分 (xià
我打算在以后使用gqlgen命令对自动生成的解析器模板进行编辑,然后创建对实际请求返回简单应用的部分。
以上是。
謝謝。
請多關照。