Продовжуємо вивчати GraphQL. Сьогодні ми розберемо, як створювати запити (queries) для отримання даних
і мутації (mutations) для зміни даних. Починаємо!
Основи роботи з Query у GraphQL
Query — це спосіб запитувати дані від API. Головна особливість GraphQL у тому, що клієнт сам визначає структуру відповіді. Це трохи схоже на похід у ресторан, де ви не замовляєте "суп", а кажете: "Мені, будь ласка, суп, але тільки без моркви і побільше картоплі".
Почнемо з простого прикладу запиту, наприклад, списку користувачів. Припустимо, у нас вже є модель користувача:
# models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
Тепер створимо GraphQL-схему для отримання даних про користувачів:
# schema.py
import graphene
from graphene_django.types import DjangoObjectType
from .models import User
class UserType(DjangoObjectType):
class Meta:
model = User
class Query(graphene.ObjectType):
all_users = graphene.List(UserType)
def resolve_all_users(root, info):
return User.objects.all()
schema = graphene.Schema(query=Query)
DjangoObjectType— це спеціальний тип, який дозволяє автоматично зв'язати нашу модель Django з GraphQL.all_users— опис запиту: він поверне список усіх користувачів.resolve_all_users— функція, яка виконує запит до бази даних і повертає дані.
Тепер ми можемо протестувати це у GraphiQL або іншому інструменті:
query {
allUsers {
name
email
}
}
У відповіді ми отримаємо:
{
"data": {
"allUsers": [
{
"name": "Alice",
"email": "alice@example.com"
},
{
"name": "Bob",
"email": "bob@example.com"
}
]
}
}
Динамічні запити: робота з параметрами
Іноді нам потрібні дані не про всіх користувачів, а про конкретного. Додамо можливість шукати користувача за ID:
class Query(graphene.ObjectType):
user_by_id = graphene.Field(UserType, id=graphene.Int(required=True))
def resolve_user_by_id(root, info, id):
return User.objects.get(pk=id)
Тепер запит може виглядати так:
query {
userById(id: 1) {
name
email
}
}
А відповідь:
{
"data": {
"userById": {
"name": "Alice",
"email": "alice@example.com"
}
}
}
звернення до неіснуючого ID призведе до виключення DoesNotExist. Цей сценарій можна обробити, повертаючи None або викидаючи зрозуміле клієнту виключення.
Основи роботи з Mutation у GraphQL
Mutation (мутація) — це спосіб змінювати дані (додавати, оновлювати або видаляти). Концептуально це схоже на HTTP POST/PUT/DELETE запити в REST, але працює гнучкіше та централізовано.
Наведемо приклад базової мутації. Додамо можливість створювати користувача через GraphQL:
class CreateUser(graphene.Mutation):
class Arguments:
name = graphene.String(required=True)
email = graphene.String(required=True)
user = graphene.Field(UserType)
def mutate(root, info, name, email):
user = User(name=name, email=email)
user.save()
return CreateUser(user=user)
class Mutation(graphene.ObjectType):
create_user = CreateUser.Field()
Як це працює:
Arguments— описує вхідні параметри для мутації.mutate— функція, що виконує логіку створення. Вона отримує параметри, створює об'єкт і повертає його.user— результат, який повернеться клієнту.
Тепер тестуємо мутацію:
mutation {
createUser(name: "Charlie", email: "charlie@example.com") {
user {
name
email
}
}
}
У відповіді ми отримуємо:
{
"data": {
"createUser": {
"user": {
"name": "Charlie",
"email": "charlie@example.com"
}
}
}
}
І запис з'являється в базі даних. Успіх!
Оновлення даних за допомогою Mutation
Окей, користувач створив свій акаунт із помилкою в імені. Давайте додамо функцію для оновлення імені користувача:
class UpdateUser(graphene.Mutation):
class Arguments:
id = graphene.Int(required=True)
name = graphene.String(required=True)
user = graphene.Field(UserType)
def mutate(root, info, id, name):
user = User.objects.get(pk=id)
user.name = name
user.save()
return UpdateUser(user=user)
class Mutation(graphene.ObjectType):
update_user = UpdateUser.Field()
Запит для оновлення:
mutation {
updateUser(id: 1, name: "Alice Updated") {
user {
name
}
}
}
І у відповіді бачимо:
{
"data": {
"updateUser": {
"user": {
"name": "Alice Updated"
}
}
}
}
запит до неіснуючого ID також може викинути виняток, тому додавання перевірки з повідомленням про помилку — гарна практика.
Видалення даних за допомогою Mutation
Додамо функцію видалення користувача:
class DeleteUser(graphene.Mutation):
class Arguments:
id = graphene.Int(required=True)
ok = graphene.Boolean()
def mutate(root, info, id):
try:
user = User.objects.get(pk=id)
user.delete()
return DeleteUser(ok=True)
except User.DoesNotExist:
return DeleteUser(ok=False)
class Mutation(graphene.ObjectType):
delete_user = DeleteUser.Field()
Тепер запит для видалення:
mutation {
deleteUser(id: 1) {
ok
}
}
Відповідь, якщо ID існує:
{
"data": {
"deleteUser": {
"ok": true
}
}
}
Тестування запитів і мутацій
Не забувай тестувати свої запити і мутації через GraphiQL або GraphQL Playground. Переконайся, що запити працюють передбачувано, а помилки повертаються в зрозумілому форматі клієнту.
Приклад помилок:
- Відсутність обов'язкового параметра: GraphQL повертає помилку про відсутній аргумент.
- Некоректний параметр: переконайся, що введені значення проходять валідацію, наприклад, email перевіряється на коректність.
Оптимізація запитів
Для підвищення швидкості, особливо при роботі з великими обсягами даних, використовуйте Django QuerySet методи select_related і prefetch_related, щоб мінімізувати кількість запитів до бази даних.
Приклад з select_related:
def resolve_all_users(root, info):
return User.objects.select_related("profile").all()
GraphQL дає можливість об'єднати запити та мутації в єдиний, потужний інструмент взаємодії між клієнтом і сервером. При правильному налаштуванні ви зможете досягти гнучкості та ефективності, про яку REST тільки мріє. Вперед до наступної лекції! 😉
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ