На попередній лекції ми дізналися, що таке складні запити у GraphQL і навіщо вони потрібні. Сьогодні ж ми розберемося, як реалізувати вкладені запити та ефективно керувати вибірками даних з GraphQL API.
Що таке вкладені запити?
Давай почнемо з базового поняття: вкладені запити у GraphQL — це запити, які дозволяють отримувати дані з пов’язаних об'єктів (наприклад, зв’язки між таблицями в базі даних). На відміну від REST API, де для складного отримання інформації може знадобитися кілька запитів, GraphQL дозволяє зробити це в одному запиті завдяки своїй декларативній природі.
Давай розглянемо приклад із реальної роботи. Припустимо, у нас є два об'єкти в базі даних: Користувач (User) і Статті (Articles). У кожного користувача може бути кілька статей (зв’язок One-to-Many). Якщо ми хочемо отримати список користувачів разом із їхніми статтями, ми можемо скористатися вкладеним запитом.
Створення вкладених запитів
Для реалізації вкладених запитів потрібно спочатку правильно описати схему GraphQL. Давай створимо об'єкти User та Article і зв'яжемо їх.
# apps/users/models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=128)
email = models.EmailField(unique=True)
class Article(models.Model):
title = models.CharField(max_length=128)
content = models.TextField()
author = models.ForeignKey(User, related_name="articles", on_delete=models.CASCADE)
Тепер створимо GraphQL-схему для цих об'єктів.
# apps/api/schema.py
import graphene
from graphene_django.types import DjangoObjectType
from apps.users.models import User, Article
# Визначення DjangoObjectType для моделі User
class UserType(DjangoObjectType):
class Meta:
model = User
# Визначення DjangoObjectType для моделі Article
class ArticleType(DjangoObjectType):
class Meta:
model = Article
# Query для отримання даних
class Query(graphene.ObjectType):
users = graphene.List(UserType) # Запит для списку користувачів
articles = graphene.List(ArticleType) # Запит для списку статей
def resolve_users(self, info, **kwargs):
return User.objects.all()
def resolve_articles(self, info, **kwargs):
return Article.objects.all()
schema = graphene.Schema(query=Query)
Виконання запиту
Тепер ми можемо виконати запит на отримання користувачів разом із їхніми статтями.
query {
users {
name
email
articles {
title
content
}
}
}
Відповідь сервера
При виконанні цього запиту сервер поверне JSON-відповідь:
{
"data": {
"users": [
{
"name": "Іван Іванов",
"email": "ivan@example.com",
"articles": [
{
"title": "Як стати програмістом",
"content": "Lorem ipsum dolor sit amet..."
},
{
"title": "Поради новачкам",
"content": "Завжди навчайтесь, використовуйте документацію..."
}
]
},
{
"name": "Аліса Смирнова",
"email": "alisa@example.com",
"articles": []
}
]
}
}
Зверни увагу, що ми не тільки отримали користувачів, але і кожну статтю, пов'язану з користувачем. Ось у чому сила GraphQL: ми визначаємо структуру відповіді у запиті.
Керування вибірками даних (Selections)
Часто виникає питання: чи повинні ми завжди отримувати всі дані? Відповідь — точно ні. GraphQL дає можливість запитувати тільки ті дані, які дійсно потрібні. Це називається керування вибірками (або Selections). У REST API ви зазвичай фіксуєте формат відповіді на стороні сервера. З GraphQL клієнт сам вирішує, що йому потрібно.
Давайте розглянемо приклад. Припустимо, нам потрібно тільки ім'я користувача та заголовок його статті. Ми легко можемо налаштувати запит:
query {
users {
name
articles {
title
}
}
}
Сервер поверне лише кілька полів:
{
"data": {
"users": [
{
"name": "Іван Іванов",
"articles": [
{
"title": "Як стати програмістом"
},
{
"title": "Поради новачкам"
}
]
},
{
"name": "Аліса Смирнова",
"articles": []
}
]
}
}
Клієнт запитує тільки те, що йому потрібно, що допомагає мінімізувати обсяг переданих даних.
Використання полів і фрагментів
Якщо ти повторюєш одні й ті самі вибірки в кількох місцях твого GraphQL-запиту, є спосіб уникнути дублювання коду. Для цього використовуються фрагменти (Fragments).
Так, замість того, щоб дублювати запит статей у кількох місцях, ми можемо винести це в окремий фрагмент:
fragment ArticleFields on ArticleType {
title
content
}
query {
users {
name
email
articles {
...ArticleFields
}
}
}
Фрагменти дозволяють покращити читабельність запиту, зробити його більш компактним, а також зручним для повторного використання.
Проблеми та підходи до їх вирішення
- Проблема N+1 запитів
Якщо ти не оптимізуєш свій GraphQL-запит, то при отриманні пов’язаних даних (наприклад, статей для кожного користувача) сервер може виконати купу окремих SQL-запитів. Це називається проблема N+1: наприклад, 1 запит для отримання користувачів і N запитів для отримання статей для кожного користувача.
Рішення: використання select_related і prefetch_related
Для оптимізації запитів у Django ми можемо використовувати методи select_related і prefetch_related.
# apps/api/schema.py
class Query(graphene.ObjectType):
users = graphene.List(UserType)
def resolve_users(self, info, **kwargs):
return User.objects.prefetch_related('articles').all()
Тепер всі статті будуть отримані одним додатковим SQL-запитом.
- Надмірна вибірка даних
Якщо клієнт запитує занадто багато даних (наприклад, всі статті у всіх користувачів), це може вплинути на продуктивність.
Рішення: обмеження даних на рівні сервера
Ти можеш обмежити кількість повернених даних, додавши додаткові фільтри:
class Query(graphene.ObjectType):
users = graphene.List(UserType, limit=graphene.Int())
def resolve_users(self, info, limit=None, **kwargs):
query = User.objects.prefetch_related('articles')
if limit:
query = query[:limit]
return query
Клієнт тепер може вказати limit для скорочення даних:
query {
users(limit: 2) {
name
articles {
title
}
}
}
Практичне завдання
Для практики спробуйте наступне:
- Додайте третю модель (наприклад, Коментарі до статей), створіть зв'язок
Article -> Commentта налаштуйте вкладені запити для отримання коментарів. - Реалізуйте управління вибірками та додайте можливість обмежити глибину вкладення (наприклад, тільки 2 рівні).
На цьому етапі ми розібралися з основами вкладених запитів та вибірок у GraphQL. У наступній лекції ми перейдемо до оптимізації цих запитів, щоб уникнути проблем із продуктивністю. А поки — тестуйте, експериментуйте та насолоджуйтесь гнучкістю GraphQL!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ