CodeGym /Courses /Module 5. Spring /Lecture 285: Working with Mutations (Mutations)

Lecture 285: Working with Mutations (Mutations)

Module 5. Spring
Level 15 , Lesson 4
Available

If query is a way to get data, then mutation is a way to change data on the server. Similar to REST methods POST, PUT, or DELETE, mutations let you add, update, or remove data.

Main differences between mutations and queries

  • Action semantics: mutations are intended to modify the server state.
  • Location in the schema: mutations are defined in a separate mutation block in the GraphQL schema.
  • Response type: like queries, mutations return data. But the returned data often contains the result of the operation (for example, the updated object).

Mutation basics

First, let's see what mutations look like in GraphQL. Example:

Example: creating a new user

GraphQL schema:


type Mutation {
    createUser(input: CreateUserInput): User
}

input CreateUserInput {
    name: String!
    email: String!
}

type User {
    id: ID!
    name: String!
    email: String!
}

Here:

  • Mutation describes the operation that modifies data.
  • Input is used to pass arguments into createUser. It's handy because it groups parameters.
  • User represents the type of the returned object.

Mutation request:


mutation {
    createUser(input: { name: "Sergey", email: "sergey@example.com" }) {
        id
        name
        email
    }
}

Server response:


{
  "data": {
    "createUser": {
      "id": "1",
      "name": "Sergey",
      "email": "sergey@example.com"
    }
  }
}

Implementing mutations in Spring Boot

Now let's implement createUser in your Spring Boot app.

Step 1: Schema definition

Create a file schema.graphqls under src/main/resources/graphql. Add the following schema:


type Mutation {
    createUser(input: CreateUserInput): User
}

input CreateUserInput {
    name: String!
    email: String!
}

type User {
    id: ID!
    name: String!
    email: String!
}

This schema describes the input data (CreateUserInput) and the returned object (User).

Step 2: Create the data model

Create a User class to represent the user entity:


package com.example.graphql.model;

public class User {
    private String id;
    private String name;
    private String email;

    // Constructors
    public User() {}

    public User(String id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // Getters and setters
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

Step 3: Implement the mutation resolver

Create a resolver class:


package com.example.graphql.resolver;

import com.example.graphql.model.User;
import org.springframework.stereotype.Component;
import java.util.UUID;

@Component
public class MutationResolver {

    public User createUser(String name, String email) {
        // Create a new user (in a real app you'd persist to the database)
        return new User(UUID.randomUUID().toString(), name, email);
    }
}

This resolver creates a new user and returns it. We use UUID to generate a unique id.

Step 4: Wire the schema to the resolver

Wire the schema to the resolver using GraphQLMutationResolver:


package com.example.graphql.resolver;

import com.example.graphql.model.User;
import graphql.kickstart.tools.GraphQLMutationResolver;
import org.springframework.stereotype.Component;
import java.util.UUID;

@Component
public class UserMutationResolver implements GraphQLMutationResolver {

    public User createUser(String name, String email) {
        // Creation logic inside the resolver
        return new User(UUID.randomUUID().toString(), name, email);
    }
}

Notes:

  • We implement the GraphQLMutationResolver interface, which automatically binds mutations to GraphQL.

Step 5: Testing mutations

Run the app and open GraphQL Playground (or another tool like Insomnia). Execute this request:


mutation {
    createUser(input: { name: "Anna", email: "anna@example.com" }) {
        id
        name
        email
    }
}

Expected response:


{
  "data": {
    "createUser": {
      "id": "cd3f7628-1137-45a4-85ba-ecfc988b0278",
      "name": "Anna",
      "email": "anna@example.com"
    }
  }
}

Error handling and validation

When it comes to mutations, it's important to handle two things:

  1. Validation of input data.
  2. Error handling.

For validation we can use the javax.validation library. For example:

Step 1: Add annotations to the Input class


import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;

public class CreateUserInput {

    @NotBlank(message = "Name cannot be blank")
    private String name;

    @Email(message = "Invalid email format")
    private String email;

    // Getters and setters...
}

Step 2: Enable validation in the resolver


import javax.validation.Valid;

@Component
public class UserMutationResolver implements GraphQLMutationResolver {

    public User createUser(@Valid CreateUserInput input) {
        return new User(UUID.randomUUID().toString(), input.getName(), input.getEmail());
    }
}

If the input is invalid the server will return a response with an error message.

Example error:


{
  "errors": [
    {
      "message": "Name cannot be blank",
      "locations": [...],
      "path": [...]
    }
  ]
}

Final tips

  1. Mutations are like POST/PUT in REST: if you've already written REST controllers, working with mutations will feel familiar.
  2. Don't forget validation: it will save you a lot of headaches when dealing with input.
  3. Handle errors smartly: return clear messages to the client so they don't have to DM you on Slack!

In the next lecture we'll dive deeper into working with Data Fetchers, which will make our queries even more flexible!

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION