NestJS 9 GraphQL 中文文档(三) - 突变

149 阅读2分钟

大部分关于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的一部分被调用了。