大部分关于GraphQL的讨论都集中在数据获取上,但任何一个完整的数据平台同样需要修改服务端数据的方法。在REST中,任何请求最终都可能对服务器造成副作用,但最佳实践建议我们不应该修改GET请求中的数据。GraphQL从技术层面上讲也是相似的,任何查询都可以被实现并引发数据写入。但是,与REST一样,建议遵守约定,即任何引发写入的操作都应通过突变显式地发送(在此阅读更多)。
代码优先
让我们给上一节(查看解析器)中用过的AuthorResolver中添加其他方法。
@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 schema:
type Mutation {
upvotePost(postId: Int!): Post
}
upvotePost() 方法接收postId(Int)作为一个参数并返回一个更新过的Post实体。正如解析器一节中解释过的原因,我们必须显式的设置期望的类型。
如果突变需要接收一个对象作为参数,我们必须创建一个输入类型。该输入类型是一个特殊种类的对象类型,它可以被传递为一个参数(在此了解更多)。要声明一个输入类型,使用@InputType() 装饰器即可。
import { InputType, Field } from '@nestjs/graphql';
@InputType()
export class UpvotePostInput {
@Field()
postId: number;
}
提示
@InputType()装饰器接口一个可选的对象作为参数,例如,你可以指定输入类型的描述。请注意,因为有TypeScript的元数据反射系统限制,你必须额外使用@Field装饰器来手动标明一个类型,或者使用命令行插件。
然后我们就可以在解析器类中使用该类型了:
@Mutation(returns => Post)
async upvotePost(
@Args('upvotePostData') upvotePostData: UpvotePostInput,
) {}
模式优先
让我们扩展在上一节中使用过的AuthorsResolver。
@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的一部分被调用了。