[Перевод] Руководство по созданию простого сервиса GraphQL с помощью Spring Boot

GraphQL

исходный адрес

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

В этой статье мы создадим простой сервер GraphQL с использованием Spring Boot.

Добавить зависимости maven

Создайте образец приложения Spring Boot и добавьте следующие зависимости.

  • graphql-spring-boot-starter

      用于启用 GraphQL 控制器,并使其在 path/graphql 中可用。它将初始化GraphQL Schema bean。
    
  • graphql-java

      可以使用易于理解的Graphql Schema 语言来编写 schema。
    
  • graphiql-spring-boot-starter

      提供图形界面,我们可以使用它来测试 GraphQL 查询和查看查询定义。
    
        <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>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphiql-spring-boot-starter</artifactId>
            <version>5.0.2</version>
        </dependency>

Следующее полное содержимое файла POM.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.techshard.graphql</groupId>
    <artifactId>springboot-graphql</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <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>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphiql-spring-boot-starter</artifactId>
            <version>5.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Создание объектов и репозиториев JPA

Далее давайте создадим простую сущность под названием Vehicle и соответствующий репозиторий JPA. Мы будем использовать Lombok, чтобы избежать написания шаблонов, таких как геттеры и сеттеры.

package com.techshard.graphql.dao.entity;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDate;

@Data
@EqualsAndHashCode
@Entity
public class Vehicle implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column(name = "type", nullable = false)
    private String type;

    @Column(name = "model_code", nullable = false)
    private String modelCode;

    @Column(name = "brand_name")
    private String brandName;

    @Column(name = "launch_date")
    private LocalDate launchDate;

    private transient  String formattedDate;

    // Getter and setter
    public String getFormattedDate() {
        return getLaunchDate().toString();
    }
}

Затем есть соответствующий репозиторий JPA.

package com.techshard.graphql.dao.repository;
import com.techshard.graphql.dao.entity.Vehicle;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface VehicleRepository extends JpaRepository<Vehicle, Integer> {
}

GraphQL Schema

GraphQL имеет собственный уникальный язык для написания схем GraphQL, который называетсяSchema Definition Language(СДЛ). Определение схемы состоит из всех функций API, доступных на конечной точке.

Типичный пример схемы GraphQL выглядит так:

type Vehicle {
	id: ID!,
	type: String,
	modelCode: String,
	brandName: String,
	launchDate: String
}

type Query {
	vehicles(count: Int):[Vehicle]
	vehicle(id: ID):Vehicle
}

type Mutation {
	createVehicle(type: String!, modelCode: String!, brandName: String, launchDate: String):Vehicle
}

Затем создайте папку в src/main/resources с именем graphql и создайте файл Vehicleql.graphqls в этой папке. Скопируйте приведенное выше и вставьте в файл Vehicleql.graphqls. Обратите внимание, что имя файла настраивается. Просто используйте .graphqls в качестве расширения файла.

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

В нашей схеме у нас есть объект Vehicle как объект домена. Тип Query представляет собой запрос, который может получать данные через сервер GraphQL. запрос является интерактивным и поддающимся изменению, и вы можете увидеть новые результаты после модификации. Структура запроса и результата одинакова. Это важно в GraphQL, потому что вы видите то, что получаете.

Мы можем увидеть некоторые примеры в действии в следующей статье.

Тип Mutation используется для представления запросов, выполняющих операции записи данных.

Root Query

Объекты Query или Mutation — это базовые объекты GraphQL, у них нет связанных классов данных. В этом случае класс распознавателя будет реализовывать GraphQLQueryResolver или GraphQLMutationResolver. Эти распознаватели будут искать методы, которые сопоставляются с полями соответствующих корневых типов.

Далее давайте определим корневой преобразователь для Vehicle.

package com.techshard.graphql.query;
import com.coxautodev.graphql.tools.GraphQLQueryResolver;
import com.techshard.graphql.dao.entity.Vehicle;
import com.techshard.graphql.service.VehicleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
public class VehicleQuery implements GraphQLQueryResolver {
    @Autowired
    private VehicleService vehicleService;
    public List<Vehicle> getVehicles(final int count) {
        return this.vehicleService.getAllVehicles(count);
    }
    public Optional<Vehicle> getVehicle(final int id) {
        return this.vehicleService.getVehicle(id);
    }
}

В этом классе у нас есть методы для получения одного объекта Vehicle и списка объектов Vehicle. Обратите внимание, что мы определили эти методы в приведенной выше схеме.

Теперь давайте определим анализатор мутаций.

package com.techshard.graphql.mutation;
import com.coxautodev.graphql.tools.GraphQLMutationResolver;
import com.techshard.graphql.dao.entity.Vehicle;
import com.techshard.graphql.service.VehicleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
@Component
public class VehicleMutation implements GraphQLMutationResolver {
    @Autowired
    private VehicleService vehicleService;
    public Vehicle createVehicle(final String type, final String modelCode, final String brandName, final String launchDate) {
        return this.vehicleService.createVehicle(type, modelCode, brandName, launchDate);
    }
}

В этом классе у нас есть только один метод для создания объекта Vehicle, который соответствует типу Mutation в определении нашей схемы.

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

package com.techshard.graphql.service;
import com.techshard.graphql.dao.entity.Vehicle;
import com.techshard.graphql.dao.repository.VehicleRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class VehicleService {
    private final VehicleRepository vehicleRepository ;
    public VehicleService(final VehicleRepository vehicleRepository) {
        this.vehicleRepository = vehicleRepository ;
    }
    @Transactional
    public Vehicle createVehicle(final String type,final String modelCode, final String brandName, final String launchDate) {
        final Vehicle vehicle = new Vehicle();
        vehicle.setType(type);
        vehicle.setModelCode(modelCode);
        vehicle.setBrandName(brandName);
        vehicle.setLaunchDate(LocalDate.parse(launchDate));
        return this.vehicleRepository.save(vehicle);
    }
    @Transactional(readOnly = true)
    public List<Vehicle> getAllVehicles(final int count) {
        return this.vehicleRepository.findAll().stream().limit(count).collect(Collectors.toList());
    }
    @Transactional(readOnly = true)
    public Optional<Vehicle> getVehicle(final int id) {
        return this.vehicleRepository.findById(id);
    }
}

тестовое приложение

Теперь мы можем протестировать приложение. Запустите приложение Spring Boot. Откройте ссылку http://localhost8080/graphiql в браузере. Мы увидим дружескую графическую встречу, как показано на изображении ниже.

В правой части пользовательского интерфейса мы также можем увидеть документацию.

Теперь мы можем попробовать выполнить следующий запрос.

mutation {
  createVehicle(type: "car", modelCode: "XYZ0192", brandName: "XYZ", launchDate: "2016-08-16") 
  {
    id
  }
}

Это создаст строку данных в таблице Vehicle. Результат:

{
  "data": {
    "createVehicle": {
      "id": "1"
    }
  }
}

Теперь давайте запустим запрос, чтобы получить данные.

query {
  vehicles(count: 1) 
  {
    id, 
    type, 
    modelCode
	}
}

тогда мы получим результат

{
  "data": {
    "vehicles": [
      {
        "id": "1",
        "type": "bus",
        "modelCode": "XYZ123"
      }
    ]
  }
}

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

В заключение

В этой статье мы рассмотрели основные концепции GraphQL.Посмотреть подробную документацию.

допустимыйGitHubНайдите полный исходный код для этого руководства на .