В предыдущей лекции мы узнали, что такое сложные запросы в 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!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ