Привет, Хабр!
GraphQL — это язык запросов к API-интерфейсам. Он отображает предоставляемые сервером данные, чтобы клиент смог выбрать именно то, что ему нужно. GraphQL SPQR призван упростить добавление GraphQL API в любой Java-проект. SPQR работает, динамически генерируя схему GraphQL из кода Java посредством аннотаций. Перейдем к самой сути!
Настройка проекта / Зависимости
Добавьте следующие зависимости в ваш maven проект:
<dependency>
<groupId>io.leangen.graphql</groupId>
<artifactId>graphql-spqr-spring-boot-starter</artifactId>
<version>0.0.6</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
Чтобы понять основные подходы SpqrAutoConfiguration
регистрирует компонент для каждой из трех встроенных ResolverBuilder
реализаций:
AnnotatedResolverBuilder
- предоставляет только методы, аннотированные@GraphQLQuery
,@GraphQLMutation
или@GraphQLSubscription
PublicResolverBuilder
- предоставляет всеpublic
методы из исходного класса операций (возвращаемые методыvoid
считаются мутациями)BeanResolverBuilder
- предоставляет все геттеры как запросы и сеттеры как мутации (возвращаемые геттерыPublisher<T>
считаются подписками)
Также возможно реализовать собственные, реализовав свой ResolverBuilder интерфейс.
Описание основных аннотаций SPQR
@GraphQLApi- аналог контроллера, в котором определены Query и Mutation.
@GraphQLQuery - QUERY, аналог read запросов
@GraphQLMutation- MUTATION, аналог create/delete/update запросов
Остальные аннотации будут описаны ниже.
Описание сущности
Сущность банковский счет имеет номер счета (numberAccount), валюту (currency) и текущий счет (balance).
@Getter
@Setter
@Entity
@Table(name = "bank_account")
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor
public class BankAccountEntity {
@Id
String numberAccount;
Currency currency;
BigDecimal balance;
}
Опишем dto для работы с этой сущностью:
@Getter
@Setter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class BankAccountDto {
@GraphQLId
String numberAccount;
@NotNull
@GraphQLScalar
@GraphQLInputField
Currency currency;
@NotNull
@GraphQLInputField
BigDecimal balance;
}
Здесь аннотация @GraphQLInputField говорит GraphQL, что текущее поле будет полем для обновления или ввода. Попрошу заметить, что у поля currency стоит аннотация @GraphQLScalar - один из способов реализовать сложный скаляр.
package ru.bank.web.dataFetcher;
import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLMutation;
import io.leangen.graphql.annotations.GraphQLNonNull;
import io.leangen.graphql.annotations.GraphQLQuery;
import io.leangen.graphql.spqr.spring.annotations.GraphQLApi;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import ru.bank.business.service.BankAccountService;
import ru.bank.web.dto.BankAccountDto;
import ru.bank.web.dto.DispatchMoneyDto;
import java.util.List;
@RequiredArgsConstructor
@Component
@GraphQLApi
public class BankAccountDataFetcher {
private final BankAccountService service;
@GraphQLQuery(name = "getAllBalance")
public List<BankAccountDto> getAll(@GraphQLArgument(name = "page")Pageable pageable) {
return service.getAll(pageable);
}
@GraphQLMutation(name = "createBankAccount", description = "create a new bank account")
public BankAccountDto createBankAccount(@GraphQLArgument(name = "createBankAccountInput") @GraphQLNonNull BankAccountDto bankAccount) {
return service.create(bankAccount);
}
@GraphQLMutation(name = "dispatchMoney", description = "dispatch money")
public void dispatchMoney(@GraphQLArgument(name = "createDispatchMoneyInput") @GraphQLNonNull DispatchMoneyDto dispatchMoneyDto) {
service.dispatchMoney(dispatchMoneyDto);
}
@GraphQLMutation(name = "removeBankAccount", description = "remove bank account")
public void removeBankAccount(@GraphQLNonNull String numberAccount) {
service.remove(numberAccount);
}
}
Параметр аннотации @GraphQLMutation name - указывает имя метода для GraphQL, а description - соответственно описание метода для graphiql
Отправка запросов
Остальной код можно посмотреть, перейдя по ссылке: https://github.com/gibkin/graphql-sqpr-spring-boot-starter
Комментарии (7)
chilicoder
09.12.2021 14:21Есть похожая библиотека на Java под названием Elide, которая позволяет следовать не только GraphQL, но и JSON-API спецификации. Не сравнивали?
JSON-API очень удобная спецификация. Хорошо описывает не только чтение, но и запись. Вот пост с сравнением: https://dri.es/headless-cms-rest-vs-jsonapi-vs-graphql
По своему опыту могу сказать, что следование JSON-API при взаимодействии FE<->BE сводит согласования только к согласованию реляционных моделей. Что очень позитивно сказывается на скорости интеграции.
js605451
Ненене, погодите, а паджинация, фильтрация, сортировка - это все руками надо делать чтоли?
gibsonen Автор
Спасибо за замечание, поправлю статью в ближайшее время
gibsonen Автор
Поправил
js605451
Я имел в виду более прямую связь между GraphQL-фасадом и источником данных. Например программист определяет источник данных на уровне "select * from BankAccounts", а SPQR берёт на себя управление частями "where", "order by" и "skip/limit".
Вот у вас написано:
Что именно SPQR делает для того, чтобы "клиент смог выбрать именно то, что ему нужно"?
gibsonen Автор
Обновлю на выходных
gibsonen Автор
Добавлю работу с querydsl