1. Обзор
GraphQL — это относительно новая концепция создания веб-сервисов в качестве альтернативы REST . Недавно появилось несколько библиотек Java для создания и вызова сервисов GraphQL.
В этом руководстве мы рассмотрим схему, запросы и мутации GraphQL. Мы увидим, как создать и имитировать простой сервер GraphQL на простой Java. Затем мы рассмотрим, как совершать вызовы сервисов GraphQL, используя известные библиотеки HTTP.
Наконец, мы также рассмотрим доступные сторонние библиотеки для вызовов службы GraphQL.
2. GraphQL
GraphQL — это язык запросов для веб-служб и среда выполнения на стороне сервера для выполнения запросов с использованием системы типов .
Сервер GraphQL определяет возможности API с помощью схемы GraphQL. Это позволяет клиенту GraphQL точно указать, какие данные извлекать из API. Это может включать дочерние ресурсы и несколько запросов в одном запросе.
2.1. Схема GraphQL
Сервер GraphQL определяет службы с набором типов. Эти типы описывают набор возможных данных, которые вы можете запросить с помощью службы .
Сервисы GraphQL могут быть написаны на любом языке. Однако схемы GraphQL необходимо определять с помощью DSL, называемого языком схем GraphQL.
В нашем примере схемы GraphQL мы определим два типа ( Book
и Author
) и одну операцию запроса для получения всех книг ( allBooks
):
type Book {
title: String!
author: Author
}
type Author {
name: String!
surname: String!
}
type Query {
allBooks: [Book]
}
schema {
query: Query
}
Тип Query
особенный, поскольку он определяет точку входа запроса GraphQL.
2.2. Запросы и мутации
Служба GraphQL создается путем определения типов и полей, а также предоставления функций для разных полей .
В своей простейшей форме GraphQL запрашивает определенные поля для объектов. Например, мы можем запросить получение всех названий книг:
{
"allBooks" {
"title"
}
}
Несмотря на то, что это выглядит похоже, это не JSON. Это специальный формат запросов GraphQL, который поддерживает аргументы, псевдонимы, переменные и многое другое.
Служба GraphQL ответит на приведенный выше запрос ответом в формате JSON следующим образом:
{
"data": {
"allBooks": [
{
"title": "Title 1"
},
{
"title": "Title 2"
}
]
}
}
В этом руководстве мы сосредоточимся на извлечении данных с помощью запросов. Однако важно упомянуть еще одно особое понятие в GraphQL — мутация.
Любая операция, которая может вызвать модификацию, отправляется с использованием типа мутации.
3. Сервер GraphQL
Давайте создадим простой сервер GraphQL на Java, используя схему, которую мы определили выше. Мы будем использовать библиотеку Java GraphQL для реализации нашего сервера GraphQL .
Мы начнем с определения нашего запроса GraphQL и реализуем метод allBooks
, указанный в нашем примере схемы GraphQL:
public class GraphQLQuery implements GraphQLQueryResolver {
private BookRepository repository;
public GraphQLQuery(BookRepository repository) {
this.repository = repository;
}
public List<Book> allBooks() {
return repository.getAllBooks();
}
}
Далее, чтобы раскрыть нашу конечную точку GraphQL, мы создадим веб-сервлет:
@WebServlet(urlPatterns = "/graphql")
public class GraphQLEndpoint extends HttpServlet {
private SimpleGraphQLHttpServlet graphQLServlet;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
graphQLServlet.service(req, resp);
}
@Override
public void init() {
GraphQLSchema schema = SchemaParser.newParser()
.resolvers(new GraphQLQuery(new BookRepository()))
.file("schema.graphqls")
.build()
.makeExecutableSchema();
graphQLServlet = SimpleGraphQLHttpServlet
.newBuilder(schema)
.build();
}
}
В методе инициализации
сервлета мы проанализируем нашу схему GraphQL, расположенную в папке ресурсов. Наконец, используя проанализированную схему, мы можем создать экземпляр SimpleGraphQLHttpServlet
.
Мы будем использовать maven-war-plugin
для упаковки нашего приложения и jetty-maven-plugin
для его запуска:
mvn jetty:run
Теперь мы готовы запустить и протестировать наш сервис GraphQL, отправив запрос по адресу:
http://localhost:8080/graphql?query={allBooks{title}}
4. HTTP-клиент
Как и в случае со службами REST, службы GraphQL предоставляются через протокол HTTP. Поэтому мы можем использовать любой HTTP-клиент Java для вызова службы GraphQL .
4.1. Отправка запросов
Давайте попробуем отправить запрос к сервису GraphQL, который мы создали в предыдущем разделе :
public static HttpResponse callGraphQLService(String url, String query)
throws URISyntaxException, IOException {
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(url);
URI uri = new URIBuilder(request.getURI())
.addParameter("query", query)
.build();
request.setURI(uri);
return client.execute(request);
}
В нашем примере мы использовали Apache HttpClient . Однако можно использовать любой HTTP-клиент Java.
4.2. Разбор ответов
Далее давайте проанализируем ответ от службы GraphQL. Сервисы GraphQL отправляют ответы в формате JSON , как и сервисы REST:
HttpResponse httpResponse = callGraphQLService(serviceUrl, "{allBooks{title}}");
String actualResponse = IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8.name());
Response parsedResponse = objectMapper.readValue(actualResponse, Response.class);
assertThat(parsedResponse.getData().getAllBooks()).hasSize(2);
В нашем примере мы использовали ObjectMapper
из популярной библиотеки Jackson . Однако мы можем использовать любую библиотеку Java для сериализации/десериализации JSON.
4.3. Насмешливые ответы
Как и в случае с любой другой службой, предоставляемой через HTTP, мы можем имитировать ответы сервера GraphQL в целях тестирования .
Мы можем использовать библиотеку MockServer для заглушки внешних HTTP-сервисов GraphQL:
String requestQuery = "{allBooks{title}}";
String responseJson = "{\"data\":{\"allBooks\":[{\"title\":\"Title 1\"},{\"title\":\"Title 2\"}]}}";
new MockServerClient(SERVER_ADDRESS, serverPort)
.when(
request()
.withPath(PATH)
.withQueryStringParameter("query", requestQuery),
exactly(1)
)
.respond(
response()
.withStatusCode(HttpStatusCode.OK_200.code())
.withBody(responseJson)
);
Наш пример фиктивного сервера примет запрос GraphQL в качестве параметра и ответит ответом JSON в теле.
5. Внешние библиотеки
Недавно появилось несколько библиотек Java GraphQL, которые позволяют еще более простые вызовы службы GraphQL.
5.1. Узлы
American Express ``
Nodes
— это клиент GraphQL от American Express, предназначенный для построения запросов из стандартных определений моделей . Чтобы начать его использовать, мы должны сначала добавить необходимую зависимость :
<dependency>
<groupId>com.github.americanexpress.nodes</groupId>
<artifactId>nodes</artifactId>
<version>0.5.0</version>>
</dependency>
В настоящее время библиотека размещена на JitPack
, который мы также должны добавить в наши установочные репозитории Maven:
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
Как только зависимость будет устранена, мы можем использовать GraphQLTemplate
для создания запроса и вызова нашей службы GraphQL:
public static GraphQLResponseEntity<Data> callGraphQLService(String url, String query)
throws IOException {
GraphQLTemplate graphQLTemplate = new GraphQLTemplate();
GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
.url(StringUtils.join(url, "?query=", query))
.request(Data.class)
.build();
return graphQLTemplate.query(requestEntity, Data.class);
}
Узлы
будут анализировать ответ от службы GraphQL, используя указанный нами класс:
GraphQLResponseEntity<Data> responseEntity = callGraphQLService(serviceUrl, "{allBooks{title}}");
assertThat(responseEntity.getResponse().getAllBooks()).hasSize(2);
Следует отметить, что Nodes
по-прежнему требует от нас создания собственных классов DTO для разбора ответа.
5.2. Java-генератор GraphQL
Библиотека GraphQL Java Generator
использует возможность генерировать код Java на основе схемы GraphQL .
Этот подход аналогичен генераторам кода WSDL, используемым в службах SOAP. Чтобы начать его использовать, мы должны сначала добавить необходимую зависимость :
<dependency>
<groupId>com.graphql-java-generator</groupId>
<artifactId>graphql-java-runtime</artifactId>
<version>1.18</version>
</dependency>
Затем мы можем настроить graphql-maven-plugin
для выполнения цели generateClientCode :
<plugin>
<groupId>com.graphql-java-generator</groupId>
<artifactId>graphql-maven-plugin</artifactId>
<version>1.18</version>
<executions>
<execution>
<goals>
<goal>generateClientCode</goal>
</goals>
</execution>
</executions>
<configuration>
<packageName>com.foreach.graphql.generated</packageName>
<copyRuntimeSources>false</copyRuntimeSources>
<generateDeprecatedRequestResponse>false</generateDeprecatedRequestResponse>
<separateUtilityClasses>true</separateUtilityClasses>
</configuration>
</plugin>
Как только мы запустим команду сборки Maven, плагин сгенерирует как DTO, так и служебные классы, необходимые для вызова нашей службы GraphQL.
Сгенерированный компонент QueryExecutor
будет содержать методы для вызова нашего сервиса GraphQL и разбора его ответа:
public List<Book> allBooks(String queryResponseDef, Object... paramsAndValues)
throws GraphQLRequestExecutionException, GraphQLRequestPreparationException {
logger.debug("Executing query 'allBooks': {} ", queryResponseDef);
ObjectResponse objectResponse = getAllBooksResponseBuilder()
.withQueryResponseDef(queryResponseDef).build();
return allBooksWithBindValues(objectResponse,
graphqlClientUtils.generatesBindVariableValuesMap(paramsAndValues));
}
Однако он создан для использования с инфраструктурой Spring .
6. Заключение
В этой статье мы рассмотрели, как сделать вызов службы GraphQL из приложения Java .
Мы научились создавать и имитировать простой сервер GraphQL. Далее мы увидели, как отправить запрос и получить ответ от сервера GraphQL, используя стандартную библиотеку HTTP. Мы также увидели, как анализировать ответы службы GraphQL из JSON в объект Java.
Наконец, мы рассмотрели две доступные сторонние библиотеки для вызовов сервисов GraphQL: Nodes
и GraphQL Java Generator
.
Как всегда, полный исходный код доступен на GitHub .