«1. Введение

GraphQL — это относительно новая концепция от Facebook, которая позиционируется как альтернатива REST для веб-API.

Эта статья познакомит вас с настройкой сервера GraphQL с использованием Spring Boot, чтобы его можно было добавить в существующие приложения или использовать в новых.

2. Что такое GraphQL?

Традиционные REST API работают с концепцией ресурсов, которыми управляет сервер. Этими ресурсами можно манипулировать некоторыми стандартными способами, следуя различным HTTP-глаголам. Это работает очень хорошо, пока наш API соответствует концепции ресурса, но быстро разваливается, когда нам нужно отклониться от него.

Это также страдает, когда клиенту нужны данные из нескольких ресурсов одновременно. Например, запрос сообщения в блоге и комментариев. Обычно это решается либо тем, что клиент делает несколько запросов, либо тем, что сервер предоставляет дополнительные данные, которые могут не всегда требоваться, что приводит к увеличению размера ответа.

GraphQL предлагает решение обеих этих проблем. Это позволяет клиенту точно указать, какие данные ему нужны, в том числе из навигации по дочерним ресурсам, в одном запросе, а также позволяет выполнять несколько запросов в одном запросе.

Он также работает в гораздо большей степени RPC, используя именованные запросы и мутации вместо стандартного обязательного набора действий. Это работает для того, чтобы поместить контроль туда, где он должен быть: разработчик API указывает, что возможно, а потребитель API — что ему нужно.

Например, блог может разрешить следующий запрос:

query {
    recentPosts(count: 10, offset: 0) {
        id
        title
        category
        author {
            id
            name
            thumbnail
        }
    }
}

Этот запрос будет:

    запрашивать десять последних сообщений для каждого сообщения, запрашивать идентификатор, заголовок и категорию для каждого сообщения, запрашивать автор, возвращающий идентификатор, имя и миниатюру

В традиционном REST API для этого требуется либо 11 запросов — 1 для сообщений и 10 для авторов — либо необходимо включить сведения об авторе в сведениях о сообщении. .

2.1. Схемы GraphQL

Сервер GraphQL предоставляет схему, описывающую API. Эта схема состоит из определений типов. Каждый тип имеет одно или несколько полей, каждое из которых принимает ноль или более аргументов и возвращает определенный тип.

График составлен из того, как эти поля вложены друг в друга. Обратите внимание, что нет необходимости в том, чтобы граф был ациклическим — циклы вполне допустимы — но он является направленным. То есть клиент может перейти от одного поля к своим дочерним, но не может автоматически вернуться к родителю, если схема не определяет это явно.

Пример схемы GraphQL для блога может содержать следующие определения, описывающие сообщение, автора сообщения и корневой запрос для получения самых последних сообщений в блоге.

type Post {
    id: ID!
    title: String!
    text: String!
    category: String
    author: Author!
}

type Author {
    id: ID!
    name: String!
    thumbnail: String
    posts: [Post]!
}

# The Root Query for the application
type Query {
    recentPosts(count: Int, offset: Int): [Post]!
}

# The Root Mutation for the application
type Mutation {
    writePost(title: String!, text: String!, category: String) : Post!
}

Знак «!» в конце некоторых имен указывает на то, что это ненулевой тип. Любой тип, не имеющий this, может иметь значение null в ответе сервера. Служба GraphQL обрабатывает их правильно, позволяя нам безопасно запрашивать дочерние поля типов, допускающих значение NULL.

Служба GraphQL также предоставляет саму схему, используя стандартный набор полей, что позволяет любому клиенту заранее запрашивать определение схемы.

Это позволяет клиенту автоматически обнаруживать изменения схемы и позволяет клиентам динамически адаптироваться к тому, как работает схема. Одним из невероятно полезных примеров этого является инструмент GraphiQL, обсуждаемый позже, который позволяет нам взаимодействовать с любым API GraphQL.

3. Представляем GraphQL Spring Boot Starter

Spring Boot GraphQL Starter предлагает фантастический способ запустить сервер GraphQL за очень короткое время. В сочетании с библиотекой GraphQL Java Tools нам нужно только написать код, необходимый для нашего сервиса.

3.1. Настройка службы

Все, что нам нужно, чтобы это работало, — это правильные зависимости:

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-spring-boot-starter</artifactId>
    <version>5.0.2</version>
</dependency>
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-tools</artifactId>
    <version>5.2.4</version>
</dependency>

Spring Boot автоматически подберет их и настроит соответствующие обработчики для автоматической работы.

«По умолчанию это будет предоставлять службу GraphQL в конечной точке /graphql нашего приложения и будет принимать запросы POST, содержащие полезные данные GraphQL. При необходимости эту конечную точку можно настроить в нашем файле application.properties.

3.2. Написание схемы

Библиотека инструментов GraphQL работает, обрабатывая файлы схемы GraphQL для создания правильной структуры, а затем связывает специальные компоненты с этой структурой. Стартер Spring Boot GraphQL автоматически находит эти файлы схемы.

Эти файлы должны быть сохранены с расширением «.graphqls» и могут присутствовать в любом месте пути к классам. У нас также может быть столько этих файлов, сколько нужно, поэтому мы можем разделить схему на модули по желанию.

Единственное требование состоит в том, что должен быть ровно один корневой запрос и не более одной корневой мутации. Это нельзя разделить по файлам, в отличие от остальной схемы. Это ограничение самого определения схемы GraphQL, а не реализации Java.

3.3. Root Query Resolver

Корневой запрос должен иметь специальные bean-компоненты, определенные в контексте Spring для обработки различных полей в этом корневом запросе. В отличие от определения схемы, нет ограничений, что для корневых полей запроса может быть только один bean-компонент Spring.

Единственное требование состоит в том, чтобы bean-компоненты реализовывали GraphQLQueryResolver и чтобы каждое поле в корневом запросе из схемы имело метод в одном из этих классов с тем же именем.

public class Query implements GraphQLQueryResolver {
    private PostDao postDao;
    public List<Post> getRecentPosts(int count, int offset) {
        return postsDao.getRecentPosts(count, offset);
    }
}

Имена метода должны быть одним из следующих в следующем порядке:

  1. <field>
  2. is<field> – only if the field is of type Boolean
  3. get<field>

Метод должен иметь параметры, соответствующие любым параметрам в схеме GraphQL, и может дополнительно принимать конечный параметр типа DataFetchingEnvironment. .

Метод также должен возвращать правильный тип возвращаемого значения для типа в схеме GraphQL, как мы сейчас увидим. Любые простые типы — String, Int, List и т. д. — могут использоваться с эквивалентными типами Java, и система просто сопоставляет их автоматически.

Выше был определен метод getRecentPosts, который будет использоваться для обработки любых запросов GraphQL для поля RecentPosts в схеме, определенной ранее.

3.4. Использование Bean-компонентов для представления типов

Каждый сложный тип на сервере GraphQL представлен Java-бином независимо от того, загружается ли он из корневого запроса или из любого другого места в структуре. Один и тот же класс Java всегда должен представлять один и тот же тип GraphQL, но имя класса не обязательно.

Поля внутри Java-бина будут напрямую отображаться на поля в ответе GraphQL на основе имени поля.

public class Post {
    private String id;
    private String title;
    private String category;
    private String authorId;
}

Любые поля или методы Java-бина, не соответствующие схеме GraphQL, будут игнорироваться, но не вызовут проблем. Это важно для работы резольверов полей.

Например, поле authorId здесь не соответствует чему-либо в нашей схеме, которую мы определили ранее, но оно будет доступно для использования на следующем шаге.

3.5. Преобразователи полей для комплексных значений

Иногда значение поля не является тривиальным для загрузки. Это может включать поиск в базе данных, сложные вычисления или что-то еще. В GraphQL Tools есть концепция преобразователя полей, который используется для этой цели. Это бины Spring, которые могут предоставлять значения вместо бина данных.

Преобразователь поля — это любой компонент в контексте Spring, который имеет то же имя, что и компонент данных, с суффиксом Resolver и реализует интерфейс GraphQLResolver. Методы в bean-компоненте распознавателя полей следуют всем тем же правилам, что и в bean-компоненте данных, но в качестве первого параметра им также предоставляется сам bean-компонент данных.

Если преобразователь полей и компонент данных имеют методы для одного и того же поля GraphQL, то преобразователь полей будет иметь приоритет.

public class PostResolver implements GraphQLResolver<Post> {
    private AuthorDao authorDao;

    public Author getAuthor(Post post) {
        return authorDao.getAuthorById(post.getAuthorId());
    }
}

Тот факт, что эти преобразователи полей загружаются из контекста Spring, важен. Это позволяет им работать с любыми другими управляемыми bean-компонентами Spring, например, с DAO.

«Важно отметить, что если клиент не запрашивает поле, то сервер GraphQL никогда не выполнит работу по его извлечению. Это означает, что если клиент извлекает сообщение и не запрашивает автора, то метод getAuthor() выше никогда не будет выполнен, и вызов DAO никогда не будет выполнен.

3.6. Значения, допускающие значение NULL

В схеме GraphQL существует концепция, согласно которой некоторые типы могут принимать значения NULL, а другие нет.

Это можно обработать в коде Java, напрямую используя нулевые значения, но в равной степени новый необязательный тип из Java 8 можно использовать непосредственно здесь для типов, допускающих значение null, и система будет делать правильные действия со значениями.

Это очень полезно, так как означает, что наш Java-код более явно совпадает со схемой GraphQL из определений методов.

3.7. Мутации

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

С точки зрения кода нет никаких причин, по которым запрос не может изменить данные на сервере. Мы могли бы легко написать преобразователи запросов, которые принимают аргументы, сохраняют новые данные и возвращают эти изменения. Это вызовет неожиданные побочные эффекты для клиентов API и считается плохой практикой.

Вместо этого следует использовать мутации, чтобы сообщить клиенту, что это приведет к изменению сохраняемых данных.

Мутации определяются в коде Java с помощью классов, реализующих GraphQLMutationResolver вместо GraphQLQueryResolver.

В противном случае применяются все те же правила, что и для запросов. Затем возвращаемое значение из поля Mutation обрабатывается точно так же, как и из поля Query, позволяя также извлекать вложенные значения.

public class Mutation implements GraphQLMutationResolver {
    private PostDao postDao;

    public Post writePost(String title, String text, String category) {
        return postDao.savePost(title, text, category);
    }
}

4. Знакомство с GraphiQL

GraphQL также имеет сопутствующий инструмент под названием GraphiQL. Это пользовательский интерфейс, который может взаимодействовать с любым сервером GraphQL и выполнять запросы и изменения к нему. Загружаемая версия существует как приложение Electron и может быть получена отсюда.

Также возможно включить веб-версию GraphiQL в наше приложение автоматически, добавив зависимость GraphiQL Spring Boot Starter:

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphiql-spring-boot-starter</artifactId>
    <version>5.0.2</version>
</dependency>

Это будет работать, только если мы размещаем наш GraphQL API по умолчанию конечная точка /graphql, поэтому потребуется отдельное приложение, если это не так.

5. Резюме

GraphQL — это очень интересная новая технология, которая потенциально может произвести революцию в способах разработки веб-API.

Сочетание Spring Boot GraphQL Starter и библиотек GraphQL Java Tools невероятно упрощает добавление этой технологии в любые новые или существующие приложения Spring Boot.

Фрагменты кода можно найти на GitHub.