Начало работы с GraphQL и Spring Boot

Начало работы с GraphQL и Spring Boot

1. Вступление

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

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

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

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

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

GraphQL offers a solution to both of these problems. Это позволяет клиенту точно указывать, какие данные требуются, в том числе от навигации по дочерним ресурсам в одном запросе, и позволяет выполнять несколько запросов в одном запросе.

Он также работает намного более RPC-способом, используя именованные запросы и мутации вместо стандартного обязательного набора действий. This works to put the control where it belongs, with the API developer specifying what is possible, and the API consumer what is desired.с

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

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!
}

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

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

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

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

The Spring Boot GraphQL Starter offers a fantastic way to get a GraphQL server running in a very short time. В сочетании с библиотекойGraphQL Java Tools нам нужно только написать код, необходимый для нашей службы.

3.1. Настройка Сервиса

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


    com.graphql-java
    graphql-spring-boot-starter
    5.0.2


    com.graphql-java
    graphql-java-tools
    5.2.4

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

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

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

Библиотека инструментов GraphQL работает, обрабатывая файлы схемы GraphQL для построения правильной структуры, а затем связывает специальные компоненты с этой структурой. The Spring Boot GraphQL starter automatically finds these schema files.

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

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

3.3. Резолвер корневых запросов

The root query needs to have special beans defined in the Spring context to handle the various fields in this root query. В отличие от определения схемы, нет никаких ограничений, что для корневых полей запроса может быть только один компонент Spring.

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

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

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

  1. <Поле>

  2. is - только если поле имеет типBoolean

  3. получить <поле>

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

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

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

3.4. Использование бинов для представления типов

Every complex type in the GraphQL server is represented by a Java bean - загружено из корневого запроса или из любого другого места в структуре. Один и тот же класс Java всегда должен представлять один и тот же тип GraphQL, но имя класса необязательно.

Fields inside the Java bean will directly map onto fields in the GraphQL response based on the name of the field.

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

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

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

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

Иногда значение поля загружается нетривиально. Это может включать в себя поиск в базе данных, сложные вычисления или что-то еще. GraphQL Tools has a concept of a field resolver that is used for this purpose. Это компоненты Spring, которые могут предоставлять значения вместо компонента данных.

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

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

public class PostResolver implements GraphQLResolver {
    private AuthorDao authorDao;

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

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

Важно отметить, чтоif the client does not request a field, then the GraphQL Server will never do the work to retrieve it. Это означает, что если клиент получает сообщение и не запрашивает автора, то указанный выше методgetAuthor() никогда не будет выполнен, и вызов DAO никогда не будет выполнен.

3.6. Обнуляемые значения

Схема GraphQL имеет концепцию, что некоторые типы могут быть обнуляемыми, а другие нет.

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

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

3.7. Мутации

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

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

Вместо этогоMutations should be used to inform the client that this will cause a change to the data being stored.

Мутации определяются в коде 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 и может быть получена изhere.

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


    com.graphql-java
    graphiql-spring-boot-starter
    5.0.2

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

5. Резюме

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

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

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