В предыдущих лекциях мы познакомились с концепцией 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. Ваш следующий шаг — научиться строить ещё более сложные запросы, добавлять фильтрацию и интегрировать аутентификацию. Прямо как в знаменитой книге "Гарри Поттер и методы рационального программирования" — чем больше вы практикуетесь, тем лучше становится ваш код!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ