В этом руководстве мы создадим Pokemon API, который использует данные из базы данных Postgres, с простой конечной точкой, которая выполняет поиск по идентификатору.
Окончательный код находится в этом Репозиторий Github .
Если у вас уже установлен Postgres локально, вы можете пропустить эту часть, в противном случае самый простой способ сделать это – запустить образ Docker. Просто установите Docker , а затем:
docker run -p5432:5432 -d postgres:11.4-alpine
Эта команда запустит экземпляр Postgres на порту 5432 с пользователем по умолчанию postgres
и базой данных по умолчанию postgres
.
Мы начнем с создания начальных файлов проекта с помощью Spring Initializr . Я выбрал:
- Градация
- Ява
- Пружинный ботинок 2.1.6
- Пускатель пружинного полотна
- Spring Data JPA
- PostgreSQL Водитель
Помимо зависимостей Spring, нам нужно добавить библиотеки GraphQL:
- GraphQL Spring Boot Starter: автоматически создаст конечную точку
/graphql
- Начальный тест GraphQL Spring Boot Starter: для наши модульные тесты
- GraphQL Java Tools: из его собственной документации : “сопоставляет поля ваших объектов GraphQL с методами и свойствами ваших объектов java”. Для этой библиотеки требуется версия
1.3. *
из Kotlin, поэтому вам нужно создать файлgradle.properties
в корневом каталоге проекта с содержимым:
kotlin.version=1.3.10
Подключение к базе данных
После добавления зависимостей вы можете отредактировать файл src/main/resources/application.properties
, чтобы добавить конфигурацию Postgres. Если вы используете приведенную выше команду Docker для локального запуска Postgres, ваш файл должен выглядеть следующим образом:
## PostgreSQL spring.datasource.url=jdbc:postgresql://localhost:5432/postgres spring.datasource.username=postgres spring.datasource.password= #drop n create table again, good for testing, comment this in production spring.jpa.hibernate.ddl-auto=create-drop
Запустите свое приложение, чтобы проверить, все ли работает до сих пор: ./gradlew bootRun
.
GraphQL имеет отличный язык схем, который добавляет объявления типов к своим запросам и возвращаемым значениям и связывает это с реализацией API. Это означает, что то, что вы объявляете в схеме, должно быть реализовано.
Если мы хотим добавить конечную точку для поиска покемона по его идентификатору, мы должны объявить в файле src/main/resources/schema.graphql
:
type Pokemon { id: ID! name: String! } type Query { pokemon(id: ID!): Pokemon }
Нашим следующим шагом теперь должен быть поиск экземпляра Pokemon в базе данных по его идентификатору, иначе приложение не запустится.
Объявленная схема ожидает возврата Pokemon type
, который содержит требуемые атрибуты id
и имя
.
Для нашего приложения это означает, что Pokemon
– это класс Java с идентификатор
и имя
свойства, а также таблица базы данных. Мы можем использовать javax.persistence
аннотации для автоматического сопоставления покемонов с таблицей базы данных со столбцами идентификатор
и имя
:
@Entity @Table(name = "pokemon") public class Pokemon { public Pokemon(final Long id, final String name) { this.id = id; this.name = name; } @Id public Long id; @Column public String name; }
Другим ожидаемым классом должен быть Spring Bean
, который реализует Распознаватель запросов GraphQL
интерфейс и должен иметь метод с именем получить покемона
, который соответствует параметрам и ответу точно так , как мы определили в схеме:
@Component public class Query implements GraphQLQueryResolver { public Pokemon getPokemon(Long id) { return new Pokemon(1L, "Pikachu"); } }
Теперь мы можем выполнить запрос на нашей новой конечной точке, чтобы проверить, является ли его ответ нашим Пикачу.
GraphiQL настраивает конечную точку в нашем API, которая позволяет нам тестировать любой запрос. В нашем проекте он будет работать по адресу http://localhost:8080/graphiql
.
В левом столбце мы должны записывать запросы, а в правом – результаты. Например, если мы введем запрос:
# Searches a Pokemon with id 25 and returns its field 'name' query { pokemon(id: 25){ name } }
Мы должны ожидать результата в правой колонке:
{ "data": { "pokemon": { "name": "Pikachu" } } }
Пока не имеет значения, какой параметр i d
мы проходим, потому что мы исправили объект ответа, но теперь мы реализуем поиск в базе данных.
В настоящее время наше приложение не выполняет реальный поиск в базе данных, а возвращает фиксированный экземпляр. Давайте теперь реализуем эту часть.
Сначала мы создаем PokemonRepository
интерфейс, который расширяет JpaRepository
:
@Repository public interface PokemonRepository extends JpaRepository{ }
Затем мы меняем наш класс Query
на автоматическое подключение этого компонента и выполняем реальную выборку базы данных:
@Component public class Query implements GraphQLQueryResolver { @Autowired private PokemonRepository repository; public Pokemon getPokemon(Long id) { // Not returning a fixed instance anymore return repository.findById(id).orElse(null); } }
Наш автоматический тест будет использовать класс GraphQLTestTemplate
, который позволяет нам вводить запрос
и проверять его ответ. Например, если мы хотим протестировать поиск pokemon по запросу id, нам сначала нужно создать файл в src/test/resources
с этим запросом:
# src/test/resources/get-pokemon-by-id.graphql query { pokemon(id: "1") { id name } }
Тестовый класс должен быть аннотирован с помощью @GraphQLTest
таким образом, он может разрешить экземпляр
GraphQLTestTemplate и
PokemonRepository должен быть помечен
@MockBean таким образом, мы можем имитировать его ответ, используя
Мокито
@RunWith(SpringRunner.class) @GraphQLTest public class DemoApplicationTests { @Autowired private GraphQLTestTemplate graphQLTestTemplate; @MockBean private PokemonRepository pokemonRepository; @Test public void getById() throws IOException { Pokemon pokemon = new Pokemon(1L, "Pikachu"); when(pokemonRepository.findById(any())) .thenReturn(Optional.of(pokemon)); GraphQLResponse response = graphQLTestTemplate.postForResource("get-pokemon-by-id.graphql"); assertTrue(response.isOk()); assertEquals("1", response.get("$.data.pokemon.id")); assertEquals("Pikachu", response.get("$.data.pokemon.name")); } }
В основном сценарий, который мы здесь тестируем, выглядит следующим образом:
- Учитывая, что репозиторий возвращает пикачу при вызове метода
findById
- Когда мы запрашиваем GraphQL Api с помощью
get-pokemon-by-id.graphql
- Затем мы ожидаем, что ответом будет JSON, содержащий пикачу из репозитория
Задача реализации GraphQL Api с использованием Spring Boot зависит в основном от конфигурации и мелких деталей функциональности Spring Boot. В целом я думаю, что интеграция работает очень хорошо, особенно инструменты GraphQL Java, которые обеспечивают реализацию кода.
Оригинал: “https://dev.to/fabiothiroki/create-a-graphql-api-using-spring-boot-32o2”