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

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

Модуль 5. Spring
Рівень 16 , Лекція 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 або повільними системами. Можна з гордістю сказати, що ти зробив крок до створення високопродуктивного додатка майбутнього!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ