Введение в GraphQL API с Java
GraphQL — это язык запросов для API, который позволяет клиентам запрашивать только те данные, которые им действительно нужны. В отличие от REST, где каждый эндпоинт возвращает фиксированную структуру данных, GraphQL даёт гибкость и уменьшает количество запросов. В этом туториале мы создадим полноценное GraphQL API на Java с использованием Spring Boot и библиотеки graphql-java. Вы узнаете, как настроить проект, определить схему, написать резолверы и обрабатывать запросы и мутации. Все примеры кода готовы к запуску.
1. Настройка проекта и зависимости
Для начала создадим новый проект Spring Boot. Мы будем использовать Maven, но вы можете адаптировать пример под Gradle. Добавьте следующие зависимости в pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>21.0</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-spring-boot-starter-webmvc</artifactId>
<version>2021-06-28T00-00-00-6e3b7b2</version>
</dependency>
После добавления зависимостей создайте основной класс приложения:
@SpringBootApplication
public class GraphqlApplication {
public static void main(String[] args) {
SpringApplication.run(GraphqlApplication.class, args);
}
}
Теперь настроим точку входа GraphQL. Создайте файл application.yml в папке resources:
graphql:
servlet:
mapping: /graphql
enabled: true
cors-enabled: true
Готово! Базовая структура проекта запускается. Далее переходим к определению схемы.
2. Определение схемы GraphQL
Схема — это ядро GraphQL API. Она описывает типы данных, запросы и мутации. Создадим файл schema.graphqls в папке src/main/resources:
type Book {
id: ID!
title: String!
author: String!
year: Int
}
type Query {
books: [Book]
bookById(id: ID!): Book
}
type Mutation {
addBook(title: String!, author: String!, year: Int): Book
deleteBook(id: ID!): Boolean
}
Здесь мы определили:
- Book — объект с полями id, title, author и year.
- Query — точки входа для чтения данных (список книг и поиск по ID).
- Mutation — операции изменения данных (добавление и удаление книги).
Теперь нам нужно загрузить эту схему в приложение. Создадим конфигурационный класс:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import java.io.File;
import java.io.IOException;
@Configuration
public class GraphqlConfig {
@Bean
public GraphQL graphQL() throws IOException {
File schemaFile = new File("src/main/resources/schema.graphqls");
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaFile);
RuntimeWiring wiring = buildWiring();
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
return GraphQL.newGraphQL(schema).build();
}
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("Query", builder -> builder
.dataFetcher("books", new BooksDataFetcher())
.dataFetcher("bookById", new BookByIdDataFetcher()))
.type("Mutation", builder -> builder
.dataFetcher("addBook", new AddBookDataFetcher())
.dataFetcher("deleteBook", new DeleteBookDataFetcher()))
.build();
}
}
3. Создание резолверов (DataFetcher)
Резолверы — это Java-классы, которые реализуют логику получения данных для каждого поля схемы. Сначала создадим простую имитацию базы данных — сервис с HashMap:
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.*;
@Service
public class BookService {
private Map<String, Book> books = new HashMap<>();
@PostConstruct
public void init() {
books.put("1", new Book("1", "Война и мир", "Лев Толстой", 1869));
books.put("2", new Book("2", "Преступление и наказание", "Фёдор Достоевский", 1866));
}
public List<Book> getAllBooks() {
return new ArrayList<>(books.values());
}
public Book getBookById(String id) {
return books.get(id);
}
public Book addBook(String title, String author, int year) {
String id = String.valueOf(books.size() + 1);
Book book = new Book(id, title, author, year);
books.put(id, book);
return book;
}
public boolean deleteBook(String id) {
return books.remove(id) != null;
}
}
Теперь создадим класс модели Book:
public class Book {
private String id;
private String title;
private String author;
private int year;
public Book(String id, String title, String author, int year) {
this.id = id;
this.title = title;
this.author = author;
this.year = year;
}
// геттеры и сеттеры
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public int getYear() { return year; }
public void setYear(int year) { this.year = year; }
}
Теперь напишем резолверы. Каждый из них реализует интерфейс DataFetcher:
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BooksDataFetcher implements DataFetcher<List<Book>> {
@Autowired
private BookService bookService;
@Override
public List<Book> get(DataFetchingEnvironment env) {
return bookService