GraphQL славиться своєю гнучкістю, адже дозволяє запитувати рівно ті дані, які потрібні, і рівно в тому форматі, в якому вони потрібні. Саме тому в реальних додатках ми часто стикаємося з необхідністю обробляти складні запити. Тож, якщо раніше ми будували прості "питаннячка" до API на кшталт "Дай мені список всіх користувачів", то тепер переходимо до створення складних "допитів" виду: "Дай мені список всіх користувачів з їхніми замовленнями, але лише за останній місяць, а ще додай коментарі до замовлень".
Навіщо потрібні складні запити?
Уявіть, що у вас є додаток для інтернет-магазину, і ви хочете отримати дані про певного користувача з його замовленнями, включаючи список товарів у кожному замовленні. У REST API вам довелося б зробити кілька запитів, щоб зібрати всю інформацію. GraphQL дозволяє зробити це за один запит! Це спрощує життя клієнту (менше запитів) і робить API більш зручним для використання.
Приклад запиту:
query {
user(id: 1) {
name
email
orders {
id
total
items {
product {
name
price
}
quantity
}
}
}
}
Цей запит дозволяє вкладено отримати дані про користувача, його замовлення та товари у кожному замовленні. Краса, правда?
Основні концепції складних запитів
Щоб створювати складні запити, потрібно розуміти кілька ключових моментів.
- З'єднання даних з декількох моделей
GraphQL легко дозволяє витягувати дані з пов'язаних моделей. Наприклад, ви можете отримати дані з таблиці користувачів, а потім одразу зв'язати її з таблицею замовлень, використовуючи зв'язок ForeignKey у Django. Усе це робиться через GraphQL-схему, без необхідності писати додатковий SQL або вигадувати велосипед.
- Обробка великих обсягів даних
У реальних застосунках дані можуть зростати як на дріжджах, і запити, які раніше виконувалися за мілісекунди, починають гальмувати сервер. У таких випадках важливо розуміти, як ефективно формувати складні запити, щоб не перевантажувати сервер і не повертати користувачу зайві дані.
Приклад неефективного запиту:
query {
users {
id
name
orders {
id
items {
id
}
}
}
}
Якщо у вас 1000 користувачів із тисячами замовлень, сервер «захлинеться» від такого запиту. Ми навчимося, як цього уникнути.
Типи складних запитів
- Запити з вкладеннями (Nested Queries)
GraphQL дозволяє запитувати дані, використовуючи вкладені рівні. Наприклад, ви можете запитати статтю та її автора, а також коментарі, написані до цієї статті.
Приклад:
query {
article(id: 1) {
title
content
author {
name
email
}
comments {
text
user {
name
}
}
}
}
Цей запит отримує дані не тільки про статтю, але й про її автора, а також про кожен коментар і коментатора.
- Запити з фільтрами
Часто в запитах потрібно використовувати фільтри, щоб отримувати тільки ті дані, які відповідають певним умовам. Наприклад, ви можете запитати замовлення, зроблені за минулий тиждень.
Приклад:
query {
orders(filter: { date_gte: "2023-01-01", date_lte: "2023-01-07" }) {
id
total
user {
name
}
}
}
Ми передали в запит аргумент filter, щоб обмежити вибірку.
Приклади складних запитів
Приклад 1: користувачі з їхніми ролями та правами доступу
Припустимо, у нас є моделі User, Role і Permission. Один користувач може мати кілька ролей, а роль може включати кілька прав.
GraphQL запит:
query {
users {
id
name
roles {
name
permissions {
name
}
}
}
}
Django модель:
class Permission(models.Model):
name = models.CharField(max_length=100)
class Role(models.Model):
name = models.CharField(max_length=100)
permissions = models.ManyToManyField(Permission)
class User(models.Model):
name = models.CharField(max_length=100)
roles = models.ManyToManyField(Role)
Результат:
{
"data": {
"users": [
{
"id": 1,
"name": "Alice",
"roles": [
{
"name": "Admin",
"permissions": [
{ "name": "Can View" },
{ "name": "Can Edit" }
]
}
]
}
]
}
}
Приклад 2: замовлення користувача з товарами
Якщо у нас є користувачі, їхні замовлення та товари в замовленнях, ми можемо написати запит такого виду:
GraphQL запит:
query {
user(id: 1) {
name
orders {
id
date
items {
product {
name
price
}
quantity
}
}
}
}
Django модель:
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
class OrderItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField()
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
items = models.ManyToManyField(OrderItem)
date = models.DateTimeField(auto_now_add=True)
Рекомендації щодо роботи зі складними запитами
Контролюйте вкладеність. Не дозволяйте клієнтам робити запити з нескінченною вкладеністю. Це можна обмежити через налаштування GraphQL двигуна (наприклад,
max_depth).Використовуйте DataLoader для оптимізації. Це допоможе боротися з проблемою N+1 запитів, про яку ми поговоримо в наступних лекціях.
Додавайте фільтри. Обмежте кількість повернених даних, щоб сервер не обробляв мільйони записів.
Використовуйте директиви. GraphQL дозволяє використовувати директиви для управління включенням або виключенням даних.
На цьому етапі у вас мало сформуватися розуміння того, навіщо взагалі потрібні складні запити, як з ними працювати і які основні підходи існують. У наступних лекціях ми заглибимося в реалізацію таких речей, як вкладені запити, вибірки даних, оптимізація і робота з великими обсягами даних.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ