JavaRush /Курсы /Модуль 5. Spring /Лекция 285: Работа с мутациями (Mutations)

Лекция 285: Работа с мутациями (Mutations)

Модуль 5. Spring
29 уровень , 4 лекция
Открыта

Если query — это способ получать данные, то mutation — это способ изменять данные на сервере. Аналогично методам POST, PUT или DELETE в REST, мутации позволяют добавлять, обновлять или удалять данные.

Основные отличия мутаций от запросов (queries)

  • Семантика действия: мутации предназначены для модификации состояния сервера.
  • Расположение в схеме: мутации определяются отдельным блоком mutation в схеме GraphQL.
  • Тип ответа: как и запросы, мутации возвращают данные. Однако возвращаемые данные часто содержат результат выполненного изменения (например, обновлённый объект).

Основы работы с мутациями

Для начала определим, как мутации выглядят в самом GraphQL. Рассмотрим пример:

Пример: создание нового пользователя

Схема GraphQL:


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

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

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

Здесь:

  • Mutation описывает операцию изменения данных.
  • Input используется для передачи аргументов в createUser. Это удобно, так как позволяет группировать параметры.
  • User представляет тип возвращаемого объекта.

Запрос мутации:


mutation {
    createUser(input: { name: "Сергей", email: "sergey@example.com" }) {
        id
        name
        email
    }
}

Ответ сервера:


{
  "data": {
    "createUser": {
      "id": "1",
      "name": "Сергей",
      "email": "sergey@example.com"
    }
  }
}

Реализация мутаций в Spring Boot

Теперь давайте реализуем createUser в вашем Spring Boot приложении.

Шаг 1: Определение схемы

Создайте файл schema.graphqls в папке src/main/resources/graphql. Добавьте следующую схему:


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

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

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

Эта схема описывает вводимые данные (CreateUserInput) и возвращаемый объект (User).

Шаг 2: Создание модели данных

Создайте класс User для представления сущности пользователя:


package com.example.graphql.model;

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

    // Конструкторы
    public User() {}

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

    // Геттеры и сеттеры
    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;
    }
}

Шаг 3: Реализация резолвера мутаций

Создайте класс резолвера:


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) {
        // Создаём нового пользователя (в реальном приложении данные шли бы в базу данных)
        return new User(UUID.randomUUID().toString(), name, email);
    }
}

Этот резолвер создаёт нового пользователя и возвращает его. Для генерации уникального id мы используем UUID.

Шаг 4: Связывание схемы и резолвера

Свяжем схему с резолвером с помощью GraphQLMutationResolver:


package com.example.graphql.resolver;

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

@Component
public class UserMutationResolver implements GraphQLMutationResolver {

    public User createUser(String name, String email) {
        // Логика создания пользователя из резолвера
        return new User(UUID.randomUUID().toString(), name, email);
    }
}

Здесь:

  • Мы реализуем интерфейс GraphQLMutationResolver, который автоматически связывает мутации с GraphQL.

Шаг 5: Тестирование мутаций

Запустите приложение и перейдите в GraphQL Playground (или другой инструмент, например Insomnia). Выполните следующий запрос:


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

Ожидаемый ответ:


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

Обработка ошибок и валидация

Когда дело доходит до мутаций, важно позаботиться о двух вещах:

  1. Валидация входных данных.
  2. Обработка ошибок.

Для валидации данных мы можем использовать библиотеку javax.validation. Например:

Шаг 1: Добавьте аннотации в Input-класс


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

public class CreateUserInput {

    @NotBlank(message = "Имя не может быть пустым")
    private String name;

    @Email(message = "Некорректный формат email")
    private String email;

    // Геттеры и сеттеры...
}

Шаг 2: Включите валидацию в резолвер


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());
    }
}

При некорректных данных сервер вернёт ответ с сообщением об ошибке.

Пример ошибки:


{
  "errors": [
    {
      "message": "Имя не может быть пустым",
      "locations": [...],
      "path": [...]
    }
  ]
}

Заключительные советы

  1. Мутации похожи на POST/PUT в REST: если вы уже писали REST-контроллеры, работа с мутациями покажется интуитивно понятной.
  2. Не забывайте про валидацию: она избавит вас от головной боли при обработке входных данных.
  3. Обрабатывайте ошибки грамотно: возвращайте клиенту понятные сообщения, чтобы им не пришлось обращаться к вашему Slack'у!

В следующей лекции мы углубимся в работу с Data Fetchers, которые помогут сделать наши запросы ещё более гибкими!

Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Артём Уровень 112
16 октября 2025
GraphQLMutationResolver в третьем спринге не поддерживается.