JavaRush /Курсы /Модуль 5. Spring /Лекция 295: Практика: Асинхронные вызовы в GraphQL с испо...

Лекция 295: Практика: Асинхронные вызовы в GraphQL с использованием `CompletableFuture`

Модуль 5. Spring
30 уровень , 4 лекция
Открыта

CompletableFuture – это мощный инструмент из библиотеки Java, который предоставляет удобный интерфейс для работы с асинхронными задачами. Его ключевые преимущества:

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

В контексте GraphQL CompletableFuture дает нам возможность:

  1. Обрабатывать запросы параллельно (например, получать данные из нескольких источников).
  2. Не блокировать серверные ресурсы в ожидании ответа от внешних API.

Шаг 1: Подготовка проекта

Для работы с CompletableFuture в GraphQL нам понадобится:

  • Приложение Spring Boot с уже настроенным Spring GraphQL.
  • Созданные базовые сущности и Data Fetchers в вашем GraphQL проекте.

Если у вас еще нет проекта, создайте минимальную структуру:


spring init --dependencies=web,graphql,data-jpa,h2 demo-graphql

Добавьте зависимости в pom.xml (если не добавлены ранее):


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>

Шаг 2: Создание схемы для асинхронного GraphQL

В файле schema.graphqls добавим запрос, который вернет список пользователей с информацией о заказах:


type User {
    id: ID!
    name: String!
    orders: [Order]!
}

type Order {
    id: ID!
    description: String!
}

type Query {
    users: [User]!
}

Шаг 3: Имплементация асинхронных Data Fetchers

1. Симуляция работы с данными

Для простоты создадим два Service-компонента:

  • Один для получения пользователей (UserService).
  • Второй для получения заказов (OrderService).

@Service
public class UserService {

    public List<User> getUsers() {
        // Симуляция получения данных
        return List.of(
                new User(1L, "Alice"),
                new User(2L, "Bob")
        );
    }
}

2. Определение Data Fetchers

В нашем GraphQL API нам нужно:

  1. Вернуть список пользователей.
  2. Для каждого пользователя подключить асинхронный вызов для получения его заказов.

@Component
public class GraphQLDataFetcher {

    private final UserService userService;
    private final OrderService orderService;

    public GraphQLDataFetcher(UserService userService, OrderService orderService) {
        this.userService = userService;
        this.orderService = orderService;
    }

    @QueryMapping
    public List<User> users() {
        return userService.getUsers();
    }

    @SchemaMapping(typeName = "User", field = "orders")
    public CompletableFuture<List<Order>> orders(User user) {
        // Асинхронный вызов для получения заказов
        return orderService.getOrdersForUser(user.getId());
    }
}

Что здесь происходит?

  1. Метод users возвращает список пользователей.
  2. Для каждого пользователя вызывается метод orders, который асинхронно загружает заказы для конкретного пользователя.

Шаг 4: Тестирование асинхронности

1. Подключение GraphQL Playground

Добавьте в application.properties:


spring.graphql.graphiql.enabled=true
spring.graphql.graphiql.path=/graphiql

Теперь вы сможете тестировать свой GraphQL API через веб-интерфейс по адресу http://localhost:8080/graphiql.

2. Выполнение запроса

Отправьте следующий запрос:


{
  users {
    id
    name
    orders {
      id
      description
    }
  }
}

Ожидаемый результат:


{
  "data": {
    "users": [
      {
        "id": "1",
        "name": "Alice",
        "orders": [
          { "id": "1", "description": "Order 1 for user 1" },
          { "id": "2", "description": "Order 2 for user 1" }
        ]
      },
      {
        "id": "2",
        "name": "Bob",
        "orders": [
          { "id": "1", "description": "Order 1 for user 2" },
          { "id": "2", "description": "Order 2 for user 2" }
        ]
      }
    ]
  }
}

Шаг 5: Тестирование асинхронного кода

Unit-тесты для CompletableFuture

Создайте простой тест для проверки асинхронного поведения:


@SpringBootTest
class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @Test
    void testGetOrdersForUser() throws Exception {
        CompletableFuture<List<Order>> future = orderService.getOrdersForUser(1L);

        // Проверяем, что данные вернулись корректно
        List<Order> orders = future.get(); // Блокируемся только для тестов
        assertEquals(2, orders.size());
        assertEquals("Order 1 for user 1", orders.get(0).getDescription());
    }
}

Интеграционные тесты для GraphQL

Используйте MockMvc для тестирования GraphQL-запросов:


@WebMvcTest(GraphQLDataFetcher.class)
class GraphQLDataFetcherTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testUsersQuery() throws Exception {
        String query = "{ users { id, name, orders { id, description } } }";
        mockMvc.perform(post("/graphql")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"query\": \"" + query + "\"}"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.users").isArray());
    }
}

Шаг 6: Практика и анализ

Как улучшить производительность?

  • Убедитесь, что тяжелые операции выполняются асинхронно.
  • Используйте ExecutorService для управления потоками.
  • Добавьте кеширование, если данные редко меняются.

Типичные ошибки:

  1. Блокировка основного потока. Используйте CompletableFuture правильно, чтобы избежать блокировки.
  2. Неправильная обработка исключений. Всегда обрабатывайте exceptionally для обработки ошибок.
  3. Незакрытые ресурсы. Асинхронные операции могут оставить открытые соединения. Убедитесь, что все ресурсы закрыты корректно.
return CompletableFuture.supplyAsync(() -> {
    // Ваш логический код
}).exceptionally(ex -> {
    // Лог ошибок
    return Collections.emptyList();
});

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

Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Артём Уровень 112
30 октября 2025
"Блокировка основного потока. Используйте CompletableFuture правильно, чтобы избежать блокировки. " - Отличный совет, очень полезный. :-) Ну и часть когда опять недодали.