我使用 graphql-codegen 插件创建了 graphql-codegen-swift-operations,这是用于 Swift 的插件
这篇文章是 GraphQL Advent Calendar 2020 的第9天的文章。
上一篇文章是由 @shinnoki 写的《如果在2021年使用GraphQL代码生成器,就使用TypedDocumentNode》。
GraphQL客户端库并非必需品。
GraphQL通常以在HTTP上进行JSON交互的方式进行实现。
换句话说,只要能够处理HTTP并解析JSON,就足够了。
在某些编程语言中,这些功能可能不需要使用库也能实现。
GraphQL客户端库主要用于缓存管理。
换句话说,如果应用程序几乎不使用缓存,则几乎不需要GraphQL客户端。
TypeScript 适用于 GraphQL 操作的代码生成情况
尽管客户不需要,但我希望有一个与响应类型相对应的 codegen 工具。此处以 TypeScript 作为最完善的工具示例。
GraphQL 提供了类型功能,可以根据 GraphQL schema 和操作(查询、变更、订阅、片段)确定类型。
type Query {
me: User!
}
type User {
id: ID!
name: String!
}
有一个名为”というschema”的结构
query {
me { name }
}
假设有一种名为「operation」的操作,如果用 TypeScript 编写该操作的响应类型,将会如下所示。
interface Data {
me: { name: string }
}
然后您可以以以下方式使用。
const res = await fetch("/graphql", {method: "POST", body: JSON.stringify({query: "query { me { name } }"})});
const data: Data = (await res.json()).data;
尽管客户端说不需要,但是从操作 (query { me { name } }) 生成的 Data 类型可以方便地检查操作返回的响应类型是否正确。如果使用 TypeScript,graphql-codegen 的 typescript-operations 正好满足这个需求。
GraphQL 操作的 Swift 代码生成情况
似乎有两种适用于Swift的codegen选项。
-
- https://github.com/apollographql/apollo-ios
- https://github.com/Shopify/graphql_swift_gen
如果不使用apollo的功能,apollo-ios生成的代码就不会很有用。相比之下,graphql_swift_gen似乎是根据模式生成的,而不是根据操作(查询、变更、订阅、片段)生成的。由于GraphQL对客户端操作的内容有所响应并且导致响应发生变化,所以使用这种方式无法获得类型带来的好处。
根据之前的例子来看,
type Query {
me: User!
}
type User {
id: ID!
name: String!
}
即使生成出与该定义直接对应的类型,
interface Query {
me: User
}
interface User {
id: string
name: string
}
不一定返回此用户所有的字段。
query {
me { name }
}
必须针对类似于此操作进行生成。
因此,我创建了一个作为GraphQL Codegen的swift和操作插件的graphql-codegen-swift-operations。
graphql-codegen-swift-operations 翻译成中文的意思是: GraphQL代码生成- Swift操作。
具体的使用方法的示例可以在https://github.com/mtsmfm/graphql-codegen-swift-operations/tree/master/swift-test-project找到。
首先,按照 GraphQL Codegen 的教程设置。
然后,执行 npm install –save-dev @mtsmfm/graphql-codegen-swift-operations,并在 codegen.yml 中使用 @mtsmfm/graphql-codegen-swift-operations 生成 generated.swift。
schema: ./schema.graphql
documents: 'graphql/*.graphql'
generates:
Sources/app/generated.swift:
- '@mtsmfm/graphql-codegen-swift-operations'
接下来只需要写操作。
query AppQuery {
...
}
当您运行 $(npm bin)/graphql-codegen 命令来执行 graphql-codegen 时,将会生成 generated.swift 文件。
class AppQuery: Decodable {
let data: Data?;
let errors: Errors?;
public static let operationDefinition: String =
"""
query AppQuery {
...
}
"""
private enum CodingKeys: String, CodingKey {
case data
case errors
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.data = try container.decode(Data.self, forKey: .data)
self.errors = try container.decodeIfPresent(Errors.self, forKey: .errors)
}
struct Data: Decodable {
let organizations: [Internal_1_Organizations]
...
}
}
只需要使用AppQuery.operationDefinition作为查询,通过POST请求,同时使用JSONDecoder和AppQuery.self来提取结果。
let client = SynchronousHTTPClient()
do {
let decoder = JSONDecoder()
let data = try client.post(url: "http://localhost:4000/graphql", json: ["query": AppQuery.operationDefinition])
let result = try decoder.decode(AppQuery.self, from: data)
if let data = result.data {
for org in data.organizations {
print(org as Any)
}
}
} catch let error {
...
}
总结
也许在使用GraphQL时,会经常从选择客户端库开始,但是如果从仅仅发送请求的角度考虑,客户端库并非必需,这样或许能够看到不同的视角。另外,在创建GraphQL Codegen插件时,
-
- 不要对schema进行处理,而是对schema + operation进行处理
- 不依赖于特定客户端库。
留意到这一点并加以注意,似乎是制作时的最佳选择。