Mutations

In GraphQL, in order to modify the server-side data, we use mutations (read more here). The official Apollo documentation shares an upvotePost() mutation example. This mutation allows to increase a post votes property value. In order to create an equivalent mutation in Nest, we’ll make use of the @Mutation() decorator.

Schema first

Let’s extend our AuthorResolver used in the previous section (see resolvers).

  1. @Resolver('Author')
  2. export class AuthorResolver {
  3. constructor(
  4. private readonly authorsService: AuthorsService,
  5. private readonly postsService: PostsService,
  6. ) {}
  7. @Query('author')
  8. async getAuthor(@Args('id') id: number) {
  9. return await this.authorsService.findOneById(id);
  10. }
  11. @Mutation()
  12. async upvotePost(@Args('postId') postId: number) {
  13. return await this.postsService.upvoteById({ id: postId });
  14. }
  15. @ResolveProperty('posts')
  16. async getPosts(@Parent() { id }) {
  17. return await this.postsService.findAll({ authorId: id });
  18. }
  19. }

Notice that we assumed that the business logic has been moved to the PostsService (respectively querying post and incrementing votes property).

Type definitions

The last step is to add our mutation to the existing types definition.

  1. type Author {
  2. id: Int!
  3. firstName: String
  4. lastName: String
  5. posts: [Post]
  6. }
  7. type Post {
  8. id: Int!
  9. title: String
  10. votes: Int
  11. }
  12. type Query {
  13. author(id: Int!): Author
  14. }
  15. type Mutation {
  16. upvotePost(postId: Int!): Post
  17. }

The upvotePost(postId: Int!): Post mutation should be available now.

Code first

Let’s add another method to the AuthorResolver used in the previous section (see resolvers).

  1. @Resolver(of => Author)
  2. export class AuthorResolver {
  3. constructor(
  4. private readonly authorsService: AuthorsService,
  5. private readonly postsService: PostsService,
  6. ) {}
  7. @Query(returns => Author, { name: 'author' })
  8. async getAuthor(@Args({ name: 'id', type: () => Int }) id: number) {
  9. return await this.authorsService.findOneById(id);
  10. }
  11. @Mutation(returns => Post)
  12. async upvotePost(@Args({ name: 'postId', type: () => Int }) postId: number) {
  13. return await this.postsService.upvoteById({ id: postId });
  14. }
  15. @ResolveProperty('posts')
  16. async getPosts(@Parent() author) {
  17. const { id } = author;
  18. return await this.postsService.findAll({ authorId: id });
  19. }
  20. }

The upvotePost() takes the postId (Int) as an input parameter and returns an updated Post entity. For the same reasons as in the resolvers section, we have to explicitly set the expected type.

If the mutation has to take an object as a parameter, we can create an input type.

  1. @InputType()
  2. export class UpvotePostInput {
  3. @Field() postId: number;
  4. }

info Hint Both @InputType() and @Field() are imported from the type-graphql package.

And then use it in the resolver class:

  1. @Mutation(returns => Post)
  2. async upvotePost(
  3. @Args('upvotePostData') upvotePostData: UpvotePostInput,
  4. ) {}