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

Spring Boot GraphQL

Это руководство для тех, кто хочет создать сервер GraphQL на Java. Требуются некоторые знания о разработке Spring Boot и Java, и хотя мы кратко рассмотрели GraphQL, основное внимание в этом руководстве уделяется разработке сервера GraphQL на Java.

Знакомство с GraphQL за 3 минуты

GraphQL — это язык запросов для получения данных с серверов. REST, SOAP и gRPC могут быть заменены в некоторых сценариях. Допустим, мы хотим получить информацию об определенной книге из бэкенда интернет-магазина.

Вы используете GraphQL для отправки следующего запроса на сервер, чтобы получить сведения о книге с идентификатором «123»:

{
  bookById(id: "book-1"){
    id
    name
    pageCount
    author {
      firstName
      lastName
    }
  }
}

Это не кусок JSON (хотя выглядит очень похоже), а запрос GraphQL. В основном это означает:

  • Запрос книги с определенным идентификатором
  • Дайте мне идентификатор, имя, количество страниц, автора этой книги
  • Для автора я хочу знать имя и фамилию

Ответ представляет собой обычный JSON:

{ 
  "bookById":
  {
    "id":"book-1",
    "name":"Harry Potter and the Philosopher's Stone",
    "pageCount":223,
    "author": {
      "firstName":"Joanne",
      "lastName":"Rowling"
    }
  }
}

Статическая типизация — одна из наиболее важных функций GraphQL: сервер точно знает, как выглядит каждый объект, который вы хотите запросить, и любой клиент может «самоанализировать» сервер и запросить «схему». Схема описывает, каким может быть запрос и какие поля вы можете вернуть. (Примечание: говоря о схеме, мы часто имеем в виду «схему GraphQL», а не что-то вроде «схемы JSON» или «схемы базы данных»)

Схема упомянутого выше запроса описывается следующим образом:

type Query {
  bookById(id: ID): Book 
}

type Book {
  id: ID
  name: String
  pageCount: Int
  author: Author
}

type Author {
  id: ID
  firstName: String
  lastName: String
}

В этом руководстве основное внимание будет уделено тому, как реализовать сервер GraphQL с этой схемой на Java.

Мы коснулись лишь некоторых основных функций GraphQL. Для получения дополнительной информации, пожалуйста, посетите официальный сайтgraphql.github.io/learn/

Предварительная версия GraphQL Java

GraphQL Javaявляется Java (серверной) реализацией GraphQL. В организации GraphQL Java Github есть несколько репозиториев Git. Одним из наиболее важных являетсяJava-движок GraphQL, который является основой всего остального.

Сам движок GraphQL Java заботится только о выполнении запросов. Он не обрабатывает темы, связанные с HTTP или JSON. Поэтому мы будем использоватьGraphQL Java Spring Bootадаптер, который предоставляет API через HTTP через Spring Boot.

Основные шаги по созданию Java-сервера GraphQL следующие:

  1. Определите схему GraphQL.
  2. Решите, как получить фактические данные, которые необходимо запросить.

Наш пример API: получение сведений о книге

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

Создайте приложение Spring Boot

Самый простой способ создать приложение Spring — использоватьstart.spring.io/"Весна Инициализ" на .

выберите:

  • Gradle Project
  • Java
  • Spring Boot 2.1.x

Для метаданных проекта мы используем:

  • Group: com.graphql-java.tutorial
  • Artifact: book-details

Что касается зависимостей, мы выбираем только Web.

нажмитеGenerate Project, вы готовы использовать свое приложение Spring Boot. Все файлы и пути, упомянутые ниже, относятся к этому проекту создания.

мы вbuild.gradleизdependenciessection добавляет в наш проект три зависимости:

Первые два — это GraphQL Java и GraphQL Java Spring, затем мы также добавилиGoogle Guava. Гуава не обязательна, но она немного облегчит нам жизнь.

Зависимости выглядят так:

dependencies {
    implementation 'com.graphql-java:graphql-java:11.0' // NEW
    implementation 'com.graphql-java:graphql-java-spring-boot-starter-webmvc:1.0' // NEW
    implementation 'com.google.guava:guava:26.0-jre' // NEW
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Schema

мыsrc/main/resourcesсоздать новый файл подschema.graphqls, который содержит следующее:

type Query {
  bookById(id: ID): Book 
}

type Book {
  id: ID
  name: String
  pageCount: Int
  author: Author
}

type Author {
  id: ID
  firstName: String
  lastName: String
}

Эта схема определяет поле верхнего уровня (вQueryтип):bookById, который возвращает сведения о конкретной книге.

Он также определяет типBook, который содержит:id,name,pageCountиauthor.authorпринадлежатьAuthorВведитеBookопределены позже.

Предметно-ориентированный язык, показанный выше для описания схем, называется языком определения схемы или SDL. Более подробную информацию можно найти наздесьоказаться.

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

мы вcom.graphqljava.tutorial.bookdetailsВ пакете создается новыйGraphQLProviderсвоего рода.initметод создаст экземпляр GraphQL:

@Component
public class GraphQLProvider {

    private GraphQL graphQL;

    @Bean
    public GraphQL graphQL() { 
        return graphQL;
    }

    @PostConstruct
    public void init() throws IOException {
        URL url = Resources.getResource("schema.graphqls");
        String sdl = Resources.toString(url, Charsets.UTF_8);
        GraphQLSchema graphQLSchema = buildSchema(sdl);
        this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
    }

    private GraphQLSchema buildSchema(String sdl) {
      // TODO: we will create the schema here later 
    }
}

Мы используемGuavaРесурсы читают файлы из пути к классам, а затем создаютGraphQLSchemaиGraphQLпример. этоGraphQLэкземпляр с использованием@BeanаннотированныйGraphQL()Методы выставляются как Spring Beans. Адаптер GraphQL Java Spring будет использоватьGraphQLinstance, чтобы наша схема могла передать URL-адрес по умолчанию/GraphQLСделайте HTTP-доступ.

Что нам еще нужно сделать, так это реализоватьbuildSchemaметод, который создаетGraphQLSchemainstance и подключите код для получения данных:

@Autowired
GraphQLDataFetchers graphQLDataFetchers;

private GraphQLSchema buildSchema(String sdl) {
    TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
    RuntimeWiring runtimeWiring = buildWiring();
    SchemaGenerator schemaGenerator = new SchemaGenerator();
    return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}

private RuntimeWiring buildWiring() {
    return RuntimeWiring.newRuntimeWiring()
        .type(newTypeWiring("Query")
        .dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
        .type(newTypeWiring("Book")
        .dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
        .build();
}

TypeDefinitionRegistry— проанализированная версия файла схемы. SchemaGenerator будетTypeDefinitionRegistryиRuntimeWiringв сочетании для фактического созданияGraphQLSchema.

buildRuntimeWiringиспользоватьgraphQLDataFetchersбоб для регистрации двухDatafetchers:

  • Одним из них является получение книг с определенным идентификатором.
  • Во-первых, найти авторов для конкретных книг.

Следующий раздел объяснитDataFetcherи как добитьсяGraphQLDataFetchersбобы.

В общем, создайтеGraphQLиGraphQLSchemaПроцесс экземпляра выглядит следующим образом:

explain

DataFetchers

Вероятно, наиболее важной концепцией Java-сервера GraphQL являетсяDatafetcher: при выполнении запросаDatafetcherПолучить данные для поля.

Когда GraphQL Java выполняет запрос, он вызывает соответствующийDatafetcher.DataFetcherэто интерфейс только с одним методом, с параметром типаDataFetcherEnvironment:

public interface DataFetcher<T> {
    T get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception;
}

ВАЖНО: Каждое поле в схеме имеетDataFetcher. Если для конкретного поля ничего не указаноDataFetcher, используйте значение по умолчаниюPropertyDataFetcher. Мы обсудим это более подробно позже.

Мы создаем новый классGraphQLDataFetchers, который содержит примерный список книг и авторов.

Полная реализация выглядит так, скоро мы рассмотрим ее подробно:

@Component
public class GraphQLDataFetchers {

    private static List<Map<String, String>> books = Arrays.asList(
            ImmutableMap.of("id", "book-1",
                    "name", "Harry Potter and the Philosopher's Stone",
                    "pageCount", "223",
                    "authorId", "author-1"),
            ImmutableMap.of("id", "book-2",
                    "name", "Moby Dick",
                    "pageCount", "635",
                    "authorId", "author-2"),
            ImmutableMap.of("id", "book-3",
                    "name", "Interview with the vampire",
                    "pageCount", "371",
                    "authorId", "author-3")
    );

    private static List<Map<String, String>> authors = Arrays.asList(
            ImmutableMap.of("id", "author-1",
                    "firstName", "Joanne",
                    "lastName", "Rowling"),
            ImmutableMap.of("id", "author-2",
                    "firstName", "Herman",
                    "lastName", "Melville"),
            ImmutableMap.of("id", "author-3",
                    "firstName", "Anne",
                    "lastName", "Rice")
    );

    public DataFetcher getBookByIdDataFetcher() {
        return dataFetchingEnvironment -> {
            String bookId = dataFetchingEnvironment.getArgument("id");
            return books
                    .stream()
                    .filter(book -> book.get("id").equals(bookId))
                    .findFirst()
                    .orElse(null);
        };
    }

    public DataFetcher getAuthorDataFetcher() {
        return dataFetchingEnvironment -> {
            Map<String,String> book = dataFetchingEnvironment.getSource();
            String authorId = book.get("authorId");
            return authors
                    .stream()
                    .filter(author -> author.get("id").equals(authorId))
                    .findFirst()
                    .orElse(null);
        };
    }
}

источник данных

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

Book DataFetcher

Наш первый способgetBookByIdDataFetcherвернутьDataFetcherреализация, которая принимаетDataFetcherEnvironmentи возвращает книгу. В данном случае это означает, что нам нужно начатьbookByIdполе принимает параметр id и находит книгу с этим конкретным идентификатором.

String bookId = dataFetchingEnvironment.getArgument("id");«id» в схеме находится в схемеbookById"id" в поле запроса:

type Query {
  bookById(id: ID): Book 
}
...

Author DataFetcher

второй способgetAuthorDataFetcherвернутьDatafetcher, чтобы получить автора конкретной книги. с ранее описанной книгойDataFetcherНапротив, у нас нет параметров, а есть экземпляр книги. из родительского поляDataFetcherРезультат можно получить,getSourceполучить. Это важная концепция для понимания: значение каждого поля в GraphQLDatafetcherвызываются сверху вниз, результатом родительского поля является дочернееDatafetcherenvironmentизsourceАтрибуты.

Затем мы получаем authorId, используя ранее загруженную книгу, и ищем конкретного автора так же, как мы ищем конкретную книгу.

Default DataFetchers

Мы реализовали только дваDatafetcher. Как и выше, если он не указан, используется значение по умолчанию.PropertyDataFetcher. В нашем случае это относится кBook.id,Book.name,Book.pageCount,Author.id,Author.firstNameиAuthor.lastNameимеет значение по умолчаниюPropertyDataFetcherсвязанные с ним.

PropertyDataFetcherПопробуйте найти свойства объектов Java несколькими способами. отjava.util.MapНапример, он просто ищет свойства по ключу. Это отлично работает для нас, потому что ключи карты книги и автора совпадают с полями, указанными в схеме. Например, в схеме, которую мы определили для типа книги, полеpageCountи книгаDataFetcherвозвращает ключ сpageCountизMap. потому что имя поля совпадает сMapтот же ключ ("pageCount"),PropertyDateFetcherнормальная работа.

Предположим, у нас есть несоответствие, книгаMapЕсть ключ, которыйtotalPagesвместоpageCount. Это приведет кpageCountзначениеnull,так какPropertyDataFetcherНе удалось получить правильное значение. Чтобы решить эту проблему, вы должныBook.pageCountзарегистрировать новыйDataFetcher. Это выглядит так:

    // In the GraphQLProvider class
    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
                .type(newTypeWiring("Query")
                        .dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
                .type(newTypeWiring("Book")
                        .dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher())
                        // This line is new: we need to register the additional DataFetcher
                        .dataFetcher("pageCount", graphQLDataFetchers.getPageCountDataFetcher()))
                .build();
    }

    // In the GraphQLDataFetchers class
    // Implement the DataFetcher
    public DataFetcher getPageCountDataFetcher() {
        return dataFetchingEnvironment -> {
            Map<String,String> book = dataFetchingEnvironment.getSource();
            return book.get("totalPages");
        };
    }
...

этоDataFetcherпройдет в книгеMapНайдите правильный ключ, чтобы решить эту проблему. (опять же: в нашем примере это не нужно, так как у нас нет несоответствия имен)

Попробуйте API

Это все, что нужно для создания работающего API GraphQL. После запуска приложения Spring Boot вы можетеhttp://localhost:8080/graphqlиспользовать API.

Самый простой способ попробовать и изучить GraphQL API — использоватьGraphQL PlaygroundИнструмент. Загрузите и запустите его.

После запуска вам будет предложено ввести URL, введитеhttp://localhost:8080/graphql.

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

demo

Полный образец исходного кода и дополнительная информация

Полный проект и полный исходный код можно найти здесь:GitHub.com/graph up — просто ав…

Дополнительную информацию о GraphQL Java можно найти по адресуДокументациянайти в.

По любым вопросам у нас также естьspectrum chat Примите обсуждение.

Для прямой обратной связи вы также можетеGraphQL Java Twitter accountНайдите нас в своем аккаунте.

Оригинальная ссылка:Getting started with GraphQL Java and Spring Boot

Ссылка на перевод:Начало работы с GraphQL Java и Spring Boot

перевести:TomorJM