GraphQL 是一种用于 API 的查询语言,它使得客户端能够精确地请求需要的数据,而不必依赖 API 返回固定的数据结构。在 GraphQL 中,字段是一种处理数据的基本方式。与 RESTful API 不同,GraphQL 通过定义查询和类型来描述数据。然而,现实世界中的数据可能是许多业务需求和查询的组合,很难去满足所有的场景。在这种情况下,模拟生成的字段(Generated Fields)提供了一种扩展 GraphQL 模式以支持特定场景的方法。
生成的字段简介
生成的字段是一种有效的方法来动态的添加 GraphQL 模式中的字段。即使数据源没有相应的输出,这些字段也可以在运行时生成。例如,当用户需要在自己的下单购物车中添加商品时,需要生成一个包含所选商品数量的字段。这时我们就要使用生成的字段。
可能的应用场景
虽然生成的字段可以应用于各种场景,但在以下情况下应用它可以得到最大的好处:
生成的字段用作解决方案
当默认情况下,API 不能满足应用程序的需求时,可以使用生成的字段作为临时解决方案。通过动态生成字段,您可以轻松获取所需的数据。
拆分大型查询
当查询结果包含大量数据时,生成的字段可以帮助您在列表中高效地呈现数据。将查询拆分为多个小型查询,每个查询都可获取特定数据的生成字段。
在模拟环境中使用
测试 GraphQL API 时,生成的字段是强大的工具。通过添加这些字段,在模拟环境中可以使用数据,其不受生产数据源的限制。
在 GraphQL 中创建生成的字段
在运行时创建生成的字段又分为不同的方式,包括计算字段和解析器字段:
计算字段
计算字段为 GraphQL 模式中的现有类型计算新的字段。它们通常用于添加不需要从数据库或其他数据源设置的功能。以下是计算字段的一个示例:
// www.javascriptcn.com code example
type Product {
id: ID!
name: String!
price: Float!
discountPrice: Float!
@computed(resolver: "computeDiscountPrice")
}
type Query {
products: [Product]!
}
type Mutation {
addProduct(name: String!, price: Float!): Product!
}
type DiscountPriceResolverResponse {
discountPrice: Float!
}
union DiscountPriceResponse =
| DiscountPriceResolverResponse
| MutationError
type DiscountPriceResolver {
discountPrice(
product: Product!
@argument(name: "quantity", type: "Int!" description: "The number of products you want to buy")
): DiscountPriceResponse!
}
extend type Product @resolver(name: "computeDiscountPrice", fields: ["discountPrice"]) {
discountPrice(
quantity: Int!
@argument(name: "quantity", type: "Int!", description: "The number of products you want to buy")
): Float! @delegate(to: "discountPrice", withArgs: { product: "$source" }, returnType: "DiscountPriceResolver")
}在上面的例子中,我们定义了一个 DiscountPriceResolver 用于计算产品的折扣价格。为了定义这个计算字段,我们需要首先将 @computed 指令添加到 Product 类型中的 discountPrice 字段上。然后我们需要定义一个名为 computeDiscountPrice 的计算字段解析器,该解析器使用 @resolver 修饰器注入到 Product 类型中。
解析器字段
解析器字段使用 GraphQL 的属性解析功能来产生生成的字段。在解析器中,您可以通过流传递数据的方式根据需要生成数据。这个在实际运用中非常实用,下面是一个解析器字段的示例:
// www.javascriptcn.com code example
type Query {
viewer: Viewer
}
interface Viewer {
id: ID!
}
type User implements Viewer {
id: ID!
name: String!
email: String!
}
extend type Viewer @resolver(name: "getCurrentViewer") {
user: User @delegate(to: "getUser", withArgs: { id: "$context.viewerId" }, returnType: "User")
}
extend type Query {
getViewerById(id: ID!): Viewer @resolver(name: "getViewerById") {
user: User @delegate(to: "getUser", withArgs: { id: "$args.id" }, returnType: "User")
}
}
type Mutation {
createUser(name: String!, email: String!): User! @resolver(name: "createUser")
}
type MutationResponse {
status: String!
}
union CreateUserResponse =
| User
| MutationError
type UserResolver {
getUser(id: ID!): User!
@resolve(
resolver: "resolveUser"
requires: { fields: ["id"] }
external: true
)
createUser(name: String!, email: String!)
: CreateUserResponse!
@resolve(
resolver: "resolveCreateUser"
requires: { fields: ["name", "email"] }
external: true
)
}
type QueryResolver {
getCurrentViewer: Viewer
@resolve(
resolver: "resolveCurrentViewer"
requires: { fields: [] }
external: true
)
getViewerById(id: ID!): Viewer
@resolve(
resolver: "resolveViewerById"
requires: { fields: [] }
external: true
)
}
type MutationResolver {
createUser(name: String!, email: String!)
: CreateUserResponse!
@resolve(
resolver: "resolveCreateUser"
requires: { fields: ["name", "email"] }
external: true
)
}
schema {
query: Query
mutation: Mutation
}
schema {
query: QueryResolver
mutation: MutationResolver
}在上述例子中,我们使用 @resolver 声明一个名为 getCurrentViewer 的解析器字段。我们还定义了一个名为 getViewerById 的解析器字段,该字段接受用户 ID 作为参数并返回匹配的用户。此外,我们使用解析器字段创建 createUser 请求,通过传递名称和电子邮件地址来创建新的用户。最后,我们使用 schema 定义了所有查询、变更(Mutation)和解析器字段。
生成的字段的指导意义
有了生成字段的支持,扩展 GraphQL 模式就是一件很轻松的事情。虽然默认 GraphQL 有着非常好的查询语言和类型系统,但您需要检查所有数据是否都在同一个 schema 中可用。因此,生成的字段确保实现满足特定需求并在必要时增强了 GraphQL API 功能。总而言之,生成的字段是 GraphQL 模式的一种有效扩展,它为我们提供了在查询时为 API 动态地添加数据的解决方案。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67cd9368e46428fe9e72c662