У світі GraphQL мутації — це, по суті, POST-запити, які дозволяють змінювати стан даних на сервері. Якщо запити (queries) використовуються для читання даних, то мутації (mutations) — це "чарівна паличка" для додавання, оновлення і видалення даних.
Мутації потрібні для того, щоб:
- Створювати нові записи в базі даних (наприклад, новий пост або користувача).
- Змінювати існуючі дані (редагувати профіль користувача).
- Видаляти записи з бази (геть нудні записи!).
Основна структура мутації в GraphQL
Перш ніж ми почнемо писати код, давайте розберемося із загальною структурою мутацій в GraphQL. Мутація в GraphQL — це, по суті, тип, який описує дію. Наприклад, якщо ви хочете створити користувача, структура може виглядати так:
mutation {
createUser(username: "admin", email: "admin@example.com") {
user {
id
username
email
}
}
}
Тут:
createUser— це ім'я мутації.usernameіemail— це вхідні параметри для створення користувача.- У блоці
userвказані ті дані, які ми хочемо отримати у відповідь.
Створення першої кастомної мутації
Давайте зробимо щось корисне, наприклад, додамо функціонал для створення нового об'єкта "Задача" (Task) у нашому проєкті. Кожна задача буде мати назву, опис і статус виконання.
Крок 1: підготовка моделі в Django
Спершу створимо модель для наших задач:
# models.py
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
is_completed = models.BooleanField(default=False)
def __str__(self):
return self.title
Не забудьте застосувати міграції, щоб модель з'явилася в базі даних:
python manage.py makemigrations
python manage.py migrate
Крок 2: створення схеми для мутації
Тепер ми додамо нашу кастомну мутацію. Мутація буде приймати title, description і is_completed як вхідні дані та створювати нову задачу.
# schema.py
import graphene
from graphene_django.types import DjangoObjectType
from .models import Task
# Визначаємо тип Task для GraphQL
class TaskType(DjangoObjectType):
class Meta:
model = Task
# Створюємо мутацію для додавання задачі
class CreateTask(graphene.Mutation):
class Arguments:
# Вхідні аргументи для мутації
title = graphene.String(required=True)
description = graphene.String(required=True)
is_completed = graphene.Boolean()
# Вихідний тип (що ми повернемо користувачеві)
task = graphene.Field(TaskType)
def mutate(self, info, title, description, is_completed=False):
# Створюємо задачу в базі даних
task = Task.objects.create(
title=title,
description=description,
is_completed=is_completed
)
return CreateTask(task=task)
Крок 3: реєстрація мутації в схемах
Додайте мутацію в кореневу схему:
# schema.py
class Mutation(graphene.ObjectType):
create_task = CreateTask.Field()
schema = graphene.Schema(mutation=Mutation)
Крок 4: тестування мутації
Запустіть сервер і протестуйте мутацію в GraphiQL або будь-якому GraphQL-клієнті:
mutation {
createTask(title: "Купити молоко", description: "Сходити в магазин і купити молоко.") {
task {
id
title
description
isCompleted
}
}
}
Якщо ви все зробили правильно, на виході ви отримаєте щось подібне до цього:
{
"data": {
"createTask": {
"task": {
"id": "1",
"title": "Купити молоко",
"description": "Сходити в магазин і купити молоко.",
"isCompleted": false
}
}
}
}
Вітаю! Ви щойно створили першу кастомну мутацію.
Додавання валідації даних
А тепер, щоб код виглядав більш професійно, додамо валідацію. Наприклад, переконаємось, що поле title не порожнє.
Змінюємо метод mutate наступним чином:
def mutate(self, info, title, description, is_completed=False):
if not title.strip():
raise Exception("Заголовок задачі не може бути порожнім!")
task = Task.objects.create(
title=title,
description=description,
is_completed=is_completed
)
return CreateTask(task=task)
Спробуйте знову надіслати запит з порожнім заголовком, і ви отримаєте помилку:
{
"errors": [
{
"message": "Заголовок задачі не може бути порожнім!",
"locations": [{ "line": 2, "column": 3 }],
"path": ["createTask"]
}
]
}
Створення складної мутації
Тепер ускладнимо задачу. Припустимо, ми хочемо, щоб одночасно створювалось завдання і призначений до нього користувач. Для цього мутація приймає два набори даних: дані завдання та ідентифікатор користувача.
Крок 1: Додавання зв'язку з користувачем
Оновіть модель Task, щоб додати зв'язок з користувачем:
# models.py
from django.contrib.auth.models import User
class Task(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
is_completed = models.BooleanField(default=False)
assigned_to = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return self.title
Зробіть міграції:
python manage.py makemigrations
python manage.py migrate
Крок 2: додавання складної мутації
Тепер додамо функціонал для створення завдання та призначення користувача:
class CreateComplexTask(graphene.Mutation):
class Arguments:
title = graphene.String(required=True)
description = graphene.String(required=True)
is_completed = graphene.Boolean()
user_id = graphene.Int(required=True)
task = graphene.Field(TaskType)
def mutate(self, info, title, description, is_completed, user_id):
user = User.objects.get(id=user_id)
if not user:
raise Exception("Користувач із вказаним ID не знайдений.")
task = Task.objects.create(
title=title,
description=description,
is_completed=is_completed,
assigned_to=user
)
return CreateComplexTask(task=task)
І зареєструйте цю мутацію в схемі:
class Mutation(graphene.ObjectType):
create_task = CreateTask.Field()
create_complex_task = CreateComplexTask.Field()
schema = graphene.Schema(mutation=Mutation)
Крок 3: тестування складної мутації
Спробуйте виконати цю мутацію:
mutation {
createComplexTask(
title: "Підготувати звіт",
description: "Зробити звіт до завтрашньої наради.",
userId: 1
) {
task {
id
title
description
isCompleted
assignedTo {
username
}
}
}
}
Тепер ви зробили не просто завдання, а вже цілу функціональність — зв'язали завдання з користувачем. Такий підхід часто використовується у реальних додатках.
Корисні поради та типові помилки
- Не забувайте про валідацію! Навіть якщо здається, що клієнт "має знати", що не можна надсилати порожні строки, завжди валідуйте вхідні дані.
- Робота з винятками. Обгортайте складні операції в
try-except, щоб обробити несподівані помилки. - Профілювання продуктивності. Використовуйте інструменти на кшталт GraphQL Playground або Apollo Server для аналізу продуктивності ваших мутацій.
- Проблема N+1 запитів. Якщо ви працюєте з кількома пов'язаними моделями, обов'язково застосовуйте оптимізацію за допомогою
select_related()абоprefetch_related().
Тепер у вас є все, щоб створювати потужні та гнучкі мутації у GraphQL.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ