У попередніх лекціях ми познайомилися з концепцією GraphQL і його перевагами. Ми навчилися встановлювати бібліотеку graphene-django у наш проєкт, налаштовувати GraphQL і запускати наш перший API. Вже зараз ми можемо створювати прості запити і тестувати їх через інструмент GraphiQL. Настав час заглибитись у саме серце GraphQL — схеми. Ми розберемося, що це таке, навіщо вони потрібні і як їх визначати у наших проєктах на Django за допомогою бібліотеки graphene-django.
Що таке схема (schema) в GraphQL?
Схема в GraphQL — це опис можливостей твого API. Вона визначає, які дані можна запитувати, змінювати і яким чином усе це пов'язано між собою. Можна сказати, що це "контракт" між сервером і клієнтом.
Схема складається з двох основних частин:
- Query — описує, які дані можна запитувати з сервера.
- Mutation — описує, які операції зміни даних можна виконувати.
Що нам дає схема:
- Ясність: схема описує все, що доступно в твоєму API, і слугує документацією за замовчуванням.
- Типізація: GraphQL вимагає суворої типізації, що дозволяє уникнути багатьох помилок на етапі розробки.
- Безпека: клієнти можуть запитувати лише те, що визначено в схемі. Це запобігає несподіваним запитам.
- Зручність тестування: інструменти на кшталт
GraphiQLчиGraphQL Playgroundможуть використовувати схему для авто-підказок і перевірки запитів.
Структура схеми
Схема складається з:
- Object Types (типи об'єктів): описують дані, доступні в API.
- Query та Mutation: визначають точки входу для запитів і мутацій.
- Параметри (Arguments): дозволяють додавати динамічність запитів.
Давай розберемося, як це виглядає в коді.
Створення схеми в Django за допомогою Graphene
Крок 1: визначення ObjectType
ObjectType — це основа для створення користувацьких типів даних. У Graphene кожен об'єкт описується за допомогою класу, похідного від graphene.ObjectType.
Приклад створення типу для нашого API:
import graphene
class Book(graphene.ObjectType):
id = graphene.ID() # Унікальний ідентифікатор книги
title = graphene.String() # Заголовок книги
author = graphene.String() # Автор книги
Тут ми визначили об'єкт Book, який містить три поля: id, title та author. Кожне з них суворо типізоване (наприклад, ID чи String).
Крок 2: визначення Query
Тепер нам потрібно дозволити клієнтам запитувати дані. Для цього створимо клас Query.
class Query(graphene.ObjectType):
hello = graphene.String() # Просте текстове поле
def resolve_hello(root, info):
return "Привіт, графісти!" # Текст, який повертається
Що тут відбувається?
- Ми додали поле
helloтипуStringу класQuery. - Додали метод
resolve_hello, який повертає дані для цього поля. Це називається "резолвер" (resolver).
Тепер запит:
{
hello
}
Поверне відповідь:
{
"data": {
"hello": "Привіт, графісти!"
}
}
Крок 3: підключення схеми до проєкту
Підключаємо нашу схему до graphene.Schema:
schema = graphene.Schema(query=Query)
А потім додаємо її в маршрути проєкту Django, щоб використовувати через GraphQL:
from django.urls import path
from graphene_django.views import GraphQLView
urlpatterns = [
path("graphql/", GraphQLView.as_view(graphiql=True, schema=schema)),
]
Тепер наш API доступний за адресою /graphql/.
Основні поняття: Query і Mutation
Query: запит даних
Query використовується для отримання інформації. Ми вже бачили простий приклад з hello. Тепер підключимося до реальної бази даних через Django.
Приклад з моделлю Book:
Модель:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
Давай створимо GraphQL-тип для цієї моделі:
from graphene_django.types import DjangoObjectType
class BookType(DjangoObjectType):
class Meta:
model = Book # Вказуємо модель
fields = ("id", "title", "author") # Вказуємо, які поля доступні
Тепер додамо тип у Query, щоб можна було запитати книги:
class Query(graphene.ObjectType):
all_books = graphene.List(BookType) # Список всіх книг
def resolve_all_books(root, info):
return Book.objects.all() # Повертаємо всі книги з бази
Тепер запит:
{
allBooks {
id
title
author
}
}
Поверне:
{
"data": {
"allBooks": [
{
"id": "1",
"title": "Джанго для новачків",
"author": "Майкл"
},
{
"id": "2",
"title": "Глибокий GraphQL",
"author": "Кейтлін"
}
]
}
}
Mutation: робота зі змінами даних
Тепер додамо можливість додавати нові книги.
class CreateBook(graphene.Mutation):
class Arguments:
title = graphene.String(required=True)
author = graphene.String(required=True)
book = graphene.Field(BookType)
def mutate(self, info, title, author):
book = Book.objects.create(title=title, author=author)
return CreateBook(book=book)
Додаємо мутацію в схему:
class Mutation(graphene.ObjectType):
create_book = CreateBook.Field()
З'єднуємо все разом:
schema = graphene.Schema(query=Query, mutation=Mutation)
Тепер запит:
mutation {
createBook(title: "Нова книга", author: "Іван") {
book {
id
title
author
}
}
}
Додасть книгу і поверне:
{
"data": {
"createBook": {
"book": {
"id": "3",
"title": "Нова книга",
"author": "Іван"
}
}
}
}
Валідація та безпека в схемах
GraphQL не менш вразливий, ніж REST. Тому:
- Не забудь перевіряти доступ користувачів на рівні резолверів.
- Додай обмеження для складних запитів, щоб клієнт не міг запросити занадто багато даних одразу.
- Використовуй бібліотеки, такі як Django Guardian, для управління дозволами на рівні об'єктів.
Приклад перевірки доступу в мутації:
class CreateBook(graphene.Mutation):
class Arguments:
title = graphene.String(required=True)
author = graphene.String(required=True)
book = graphene.Field(BookType)
def mutate(self, info, title, author):
if not info.context.user.is_authenticated:
raise Exception("Ви повинні бути авторизовані для додавання книги.")
book = Book.objects.create(title=title, author=author)
return CreateBook(book=book)
Якщо користувач не аутентифікований, GraphQL відправить помилку.
Що далі?
Тепер ви знаєте, як створювати складні та гнучкі схеми в GraphQL. Ваш наступний крок — навчитися будувати ще більш складні запити, додавати фільтрацію та інтегрувати автентифікацію. Прямо як у знаменитій книзі "Гаррі Поттер і методи раціонального програмування" — чим більше ви практикуєтеся, тим кращим стає ваш код!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ