JavaRush /Курси /Модуль 5. Spring /Лекція 286: Введення в Data Fetchers та їх роль у GraphQL...

Лекція 286: Введення в Data Fetchers та їх роль у GraphQL

Модуль 5. Spring
Рівень 15 , Лекція 5
Відкрита

Уяви, що ти в ресторані: офіціант приносить меню, ти обираєш страви. Офіціант записує твоє замовлення, біжить на кухню, де шеф готує страви. Потім він приносить їх назад. У світі GraphQL Data Fetchers — це ті офіціанти, які "приносять дані" для запитів. Вони виконують запити, дістають дані з баз, REST API чи інших джерел і повертають їх клієнту.

Data Fetcher — це механізм, що відповідає за те, як дані, описані в GraphQL схемі, будуть отримані. Він — міст між твоєю схемою і реальними даними.


Чому Data Fetchers такі важливі?

Коли клієнт робить запит до GraphQL API, сервер звертається до відповідного Data Fetcher'у для кожного поля в запиті. На відміну від REST або звичних сервісів, де ти зазвичай робиш одну точку доступу для повернення даних, GraphQL вимагає більш тонкого підходу: для кожного поля може бути власна логіка отримання даних.

Data Fetchers дозволяють гнучко налаштувати звідки і як саме будуть братися дані: з бази даних, інших API, кешу або навіть оброблені локально.


Різниця між стандартними резолверами і Data Fetchers

У попередніх лекціях ми вже знайомились з Query і Mutation Resolvers. Насправді ці резолвери — базові Data Fetchers, які обробляють запити на верхньому рівні. Проте, якщо заглибитись, стає ясно, що Data Fetchers дають набагато більше контролю при визначенні деталей обробки.

Коли використовувати Query Resolvers, а коли перейти на Data Fetchers? Ось кілька випадків:

  • Якщо твої дані залежать від полів всередині типу (наприклад, треба розрахувати щось на основі отриманих даних), тоді потрібні Data Fetchers.
  • Якщо дані для різних полів в одному типі мають надходити з різних джерел.
  • Якщо потрібна кастомна логіка саме для конкретних полів або ти інтегруєш дані з багатьох джерел.

Практичне використання Data Fetchers

1. Реалізація базового Data Fetcher

Почнемо з класичного простого прикладу. Припустимо, що у нас є об'єкт Book в системі. У GraphQL схемі це може виглядати так:


type Book {
    id: ID!
    title: String!
    author: Author!
}

Тип Author:


type Author {
    id: ID!
    name: String!
}

І GraphQL запит:


query {
    book(id: 1) {
        title
        author {
            name
        }
    }
}

У цьому випадку title і author.name мають оброблятися через Data Fetchers.

1.1 Визначаємо наш Data Fetcher для title


@Component
public class TitleDataFetcher implements DataFetcher<String> {

    private final BookService bookService;

    public TitleDataFetcher(BookService bookService) {
        this.bookService = bookService;
    }

    @Override
    public String get(DataFetchingEnvironment environment) throws Exception {
        // Отримуємо ID книги з аргументів запиту
        Integer bookId = environment.getArgument("id");
        Book book = bookService.getBookById(bookId);
        return book.getTitle();
    }
}

Тут метод get отримує DataFetchingEnvironment, який дає всю інформацію про поточний запит: аргументи, контекст тощо.

1.2 Налаштовуємо Data Fetcher у GraphQL Schema

Створимо schema.graphqls:


type Query {
    book(id: ID!): Book
}

type Book {
    id: ID!
    title: String!
    author: Author!
}

type Author {
    id: ID!
    name: String!
}

Підключаємо Data Fetcher у RuntimeWiring:


@Configuration
public class GraphQLConfiguration {

    @Bean
    public RuntimeWiringConfigurer runtimeWiringConfigurer(TitleDataFetcher titleDataFetcher) {
        return wiring -> wiring
                .type("Book", builder -> builder
                    .dataFetcher("title", titleDataFetcher));
    }
}

2. Інтеграція зовнішніх джерел (наприклад, REST API)

Уяви, що author — це окремий мікросервіс, який повертає дані про автора. Тепер створимо Data Fetcher для Author.


@Component
public class AuthorDataFetcher implements DataFetcher<Author> {

    private final RestTemplate restTemplate;

    public AuthorDataFetcher(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public Author get(DataFetchingEnvironment environment) throws Exception {
        // Отримуємо батьківський об'єкт (Book)
        Book book = environment.getSource();
        // Отримуємо автора книги з іншого сервісу
        ResponseEntity<Author> response = restTemplate.getForEntity(
                "http://author-service/authors/" + book.getAuthorId(), Author.class);
        return response.getBody();
    }
}

3. Кастомна обробка даних

Іноді потрібно додати кастомну логіку. Наприклад, треба повернути поле rating, яке обчислюється динамічно.


@Component
public class RatingDataFetcher implements DataFetcher<Double> {

    @Override
    public Double get(DataFetchingEnvironment environment) throws Exception {
        // Генеруємо випадковий рейтинг від 1 до 5
        return Math.random() * 5 + 1;
    }
}

Складні випадки: коли Data Fetchers реально допомагають

1. Агрегація даних з кількох джерел

Припустимо, твій GraphQL запит запитує поле reviews для книги, яке знаходиться в іншій базі. Замість того, щоб змінювати основний BookResolver, можна створити окремий Data Fetcher для reviews.


@Component
public class ReviewsDataFetcher implements DataFetcher<List<Review>> {

    private final ReviewsService reviewsService;

    public ReviewsDataFetcher(ReviewsService reviewsService) {
        this.reviewsService = reviewsService;
    }

    @Override
    public List<Review> get(DataFetchingEnvironment environment) throws Exception {
        // Отримуємо батьківський об'єкт (Book)
        Book book = environment.getSource();
        return reviewsService.getReviewsForBook(book.getId());
    }
}

2. Кілька Data Fetchers на одному полі

Іноді захочеш підставляти різні Data Fetchers залежно від умов. Наприклад, поле price може братися з різних джерел:

  • Якщо це книга на складі — з локальної бази.
  • Якщо це цифрова копія — з зовнішнього API.

Типові помилки

Працюючи з Data Fetchers, новачки часто роблять такі помилки. Наприклад, забувають про оптимізацію запитів. Якщо ти викликаєш 10 Data Fetchers для одного запиту, і кожен з них робить звернення в базу, це може призвести до серйозних проблем з продуктивністю. Рішення — використовувати batch loaders (про них поговоримо пізніше), щоб мінімізувати кількість запитів.

Також часто забувають про обробку виключень. Якщо твій Data Fetcher кидає виключення, це може "завалити" увесь запит. Використовуй DataFetcherExceptionHandler для обробки.


Підбиваючи підсумки

Data Fetchers — ключовий інструмент у GraphQL, який зв'язує твою схему з джерелами даних і дозволяє обробляти кожен запит максимально гнучко. З їх допомогою можна не лише масштабувати додаток, але й суттєво покращити продуктивність і гнучкість, особливо в мікросервісній архітектурі.

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