Статьи Утилиты 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.


Облако тэгов

Kotlin, Java, Spring, Spring Boot, Spring Data, SQL, PostgreSQL, Oracle, Linux, Hibernate, Collections, Stream API, многопоточность, файлы, Nginx, Apache, maven, gradle, JUnit, YouTube, новости, руководство, ООП, алгоритмы, головоломки, rest, GraphQL, Excel, XML, json, yaml.

Последние статьи


Комментарии

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

×

devmark.ru