Статьи
Утилиты Telegram YouTube Отзывы

Мутации в GraphQL

Исходники

5 апреля 2024

Тэги: Collections, GraphQL, json, Kotlin, Spring Boot.

Содержание

  1. Мутации
  2. Слой хранения данных
  3. Контроллер мутаций
  4. Тестируем мутацию

В прошлой статье GraphQL в Spring Boot мы создали приложение на Kotlin с API в формате GraphQL, в котором есть две сущности: Книга и Автор. Также научились расширять стандартный набор типов graphql и решили «проблему N+1» с помощью пакетной подгрузки дочерних сущностей. Исходники проекта доступны на github.

Теперь давайте рассмотрим как можно менять данные с помощью GraphQL.

Мутации

Для изменения данных в GraphQL используются так называемые «мутации». По сути это обычные методы со входными и выходными параметрами, доступные через протокол взаимодействия GraphQL.

Мутация – изменение данных в GraphQL

Давайте рассмотрим создание мутации на конкретном примере. Доработаем наше приложение, в котором есть сущности «Книга» и «Автор» таким образом, чтобы можно было создавать новых авторов. Чуть позже мы увидим, что сама логика создания инкапсулируется в обычном методе, поэтому все виды изменения данных (создание, обновление, удаление) делаются привычным образом.

Сначала добавим новый тип в нашу graphql-схему (файл schema.graphqls):

# Публичные методы GraphQL для изменения данных
type Mutation {
    # Добавление нового автора
    createAuthor(author: AuthorCreationRequest!): Author!
}

В схеме должен быть объявлен один тип с именем Mutation, внутри которого, по аналогии с типом Query, собраны все методы-мутации. Входные параметры методов, используемые в мутациях, должны быть объявлены с ключевым словом input:

input AuthorCreationRequest {
    # Имя автора
    firstName: String!
    # Фамилия автора
    lastName: String!
    # Дата рождения
    birthDate: Date!
}

В нашем случае мы создаём новый input под названием AuthorCreationRequest, который содержит все поля из сущности Author, кроме id. Вновь созданная сущность возвращается уже как Author, которую мы определяли ранее:

# Автор книги
type Author {
    id: ID!
    # Имя автора
    firstName: String!
    # Фамилия автора
    lastName: String!
    # Дата рождения
    birthDate: Date!
}

Этих изменений в схеме достаточно, поэтому перейдём к написанию кода.

Слой хранения данных

Сначала создадим data-класс AuthorCreationRequest один в один как мы определили его в схеме:

data class AuthorCreationRequest(
    val firstName: String,
    val lastName: String,
    val birthDate: LocalDate,
)

Затем доработаем AuthorRepository, который представляет собой слой взаимодействия с хранилищем данных. В реальном проекте тут должно быть взаимодействие с БД, но мы для простоты храним объекты в памяти, т.к. JDBC API выходит за рамки данной статьи.

Добавим в репозиторий метод createAuthor():

fun createAuthor(dto: AuthorCreationRequest): Author {
    val author = Author(
        id = AUTHORS.maxOf { it.key } + 1,
        firstName = dto.firstName,
        lastName = dto.lastName,
        birthDate = dto.birthDate,
    )
    AUTHORS[author.id] = author // MutableMap<Int, Author>
    return author
}

Тут мы создаём новый объект типа Author, инициализируя его полями из запроса. Также назначаем id подобно реальной базе данных: ищем максимальный id из уже созданных и увеличиваем его на 1. Наконец, добавляем в мапу AUTHORS новый объект с ключом, равным его id. Обратите внимание, что мапа должна иметь тип MutableMap, т.к. в прошлой статье она была обычной Map, т.е. только для чтения.

В принципе, вы можете реализовать какую угодно логику внутри метода createAuthor(). GraphQL лишь обеспечивает вам его вызов с параметром определённого типа.

Контроллер мутаций

Наконец, создадим новый спринговый контроллер MutationController, в котором удобно держать все мутации. Хотя технически мы могли бы использовать уже созданный ранее контроллер с запросами на чтение.

@Controller
class MutationController(
    private val authorRepository: AuthorRepository,
) {

    @MutationMapping("createAuthor")
    fun createAuthor(@Argument("author") dto: AuthorCreationRequest): Author =
        authorRepository.createAuthor(dto)
}

Здесь мы внедряем в контроллер слой взаимодействия с БД. В реальном проекте тут должен быть сервисный слой, но он бы просто проксировал вызов метода, поэтому пропустим его для краткости.

Сам метод createAuthor() помечается аннотацией @MutationMapping. В скобках явно указано имя метода из схемы graphql, но в данном случае это делать не обязательно, т.к. они совпадают. Входной параметр AuthorCreationRequest помечается аннотацией @Argument и в скобках указывается имя этого параметра из схемы.

Тестируем мутацию

Теперь запустим проект и перейдём на эндпоинт 127.0.0.1:8080/graphiql, где сможем вызвать мутацию с помощью отладочного веб-интерфейса graphiql.

Вызов мутации через graphiql

В отличие от запросов на чтение данных, которые начинаются с ключевого слова query, запросы мутаций начинаются со слова mutation. Далее в фигурных скобках указывается имя метода, ему полностью передаются все input-параметры. А вот в качестве результата работы метода можно запросить не всё подряд, только нужные поля.

Как видите, создание мутаций в graphql даже проще, чем чтение данных, т.к. здесь нет «проблемы N+1». По сути происходит обычное проксирование вызова метода. Поэтому логика изменения данных не будет сильно отличаться от привычного нам restful API.



Комментарии

Добавить комментарий

×

devmark.ru