GraphQL 的大多数讨论都集中在数据获取上,但任何完整的数据平台也需要一种修改服务器端数据的方法。在 REST 中,任何请求都可能最终对服务器产生副作用,但最佳实践表明我们不应该修改 GET 请求中的数据。GraphQL 类似 - 从技术上讲,任何查询都可以实现以导致数据写入。但是,与 REST 一样,建议遵守惯例,即任何导致写入的操作都应通过突变明确发送(阅读更多信息 此处)。
官方 Apollo 文档使用 upvotePost()
突变示例。此Mutations
实现了一种增加帖子 votes
属性值的方法。要在 Nest 中创建等效突变,我们将使用 @Mutation()
装饰器。
代码优先
让我们为上一节中使用的AuthorResolver
添加另一个方法(参见resolvers)。
@Mutation(returns => Post)
async upvotePost(@Args({ name: 'postId', type: () => Int }) postId: number) {
return this.postsService.upvoteById({ id: postId });
}
所有装饰器(例如 @Resolver
、@ResolveField
、@Args
等)均从 @nestjs/graphql
包中导出。
这将导致在 SDL 中生成 GraphQL 模式的以下部分:
type Mutation {
upvotePost(postId: Int!): Post
}
upvotePost()
方法以 postId
(Int
)作为参数并返回更新的 Post
实体。出于 resolvers 部分中解释的原因,我们必须明确设置预期类型。
如果突变需要将对象作为参数,我们可以创建一个 输入类型。输入类型是一种特殊的对象类型,可以作为参数传入(阅读更多信息,请点击此处)。要声明输入类型,请使用 @InputType()
装饰器。
import { Field, InputType } from '@nestjs/graphql'
@InputType()
export class UpvotePostInput {
@Field()
postId: number
}
@InputType()
装饰器将选项对象作为参数,因此您可以指定输入类型的描述。请注意,由于 TypeScript 的元数据反射系统限制,您必须使用 @Field
装饰器手动指示类型,或者使用 CLI 插件。
然后我们可以在解析器类中使用此类型:
@Mutation(returns => Post)
async upvotePost(
@Args('upvotePostData') upvotePostData: UpvotePostInput,
) {}
Schema 优先
让我们扩展上一节中使用的 AuthorResolver
(参见 resolvers)。
@Mutation()
async upvotePost(@Args('postId') postId: number) {
return this.postsService.upvoteById({ id: postId });
}
请注意,我们上面假设业务逻辑已移至 PostsService
(查询帖子并增加其 votes
属性)。PostsService
类中的逻辑可以根据需要简单或复杂。此示例的重点是展示解析器如何与其他提供程序交互。
最后一步是将我们的变异添加到现有类型定义中。
type Author {
id: Int!
firstName: String
lastName: String
posts: [Post]
}
type Post {
id: Int!
title: String
votes: Int
}
type Query {
author(id: Int!): Author
}
type Mutation {
upvotePost(postId: Int!): Post
}
现在可以将 upvotePost(postId: Int!): Post
突变作为我们应用程序的 GraphQL API 的一部分来调用。