JavaRush /Курсы /Модуль 3: Django /Определение схемы (schema) в GraphQL

Определение схемы (schema) в GraphQL

Модуль 3: Django
24 уровень , 3 лекция
Открыта

В предыдущих лекциях мы познакомились с концепцией GraphQL и его преимуществами. Мы научились устанавливать библиотеку graphene-django в наш проект, настраивать GraphQL и запускать наше первое API. Уже сейчас мы можем создавать простые запросы и тестировать их через инструмент GraphiQL. Настало время углубиться в самое сердце GraphQL — схемы. Мы разберёмся, что это такое, зачем они нужны и как их определять в наших проектах на Django с помощью библиотеки graphene-django.

Что такое схема (schema) в GraphQL?

Схема в GraphQL — это описание возможностей вашего API. Она определяет, какие данные можно запрашивать, изменять и каким образом всё это связано между собой. Можно сказать, что это "контракт" между сервером и клиентом.

Схема состоит из двух основных частей:

  1. Query — описывает, какие данные можно запрашивать с сервера.
  2. Mutation — описывает, какие операции изменения данных можно выполнять.

Что нам даёт схема:

  1. Ясность: схема описывает всё, что доступно в вашем API, и служит документацией по умолчанию.
  2. Типизация: GraphQL требует строгой типизации, что позволяет избежать множества ошибок на этапе разработки.
  3. Безопасность: клиенты могут запрашивать только то, что определено в схеме. Это предотвращает неожиданные запросы.
  4. Удобство тестирования: инструменты вроде 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 "Привет, графисты!"  # Возвращаемый текст

Что тут происходит?

  1. Мы добавили поле hello типа String в класс Query.
  2. Добавили метод 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. Поэтому:

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

1
Задача
Модуль 3: Django, 24 уровень, 3 лекция
Недоступна
Создание простого запроса (Query)
Создание простого запроса (Query)
1
Задача
Модуль 3: Django, 24 уровень, 3 лекция
Недоступна
Определение пользовательского типа
Определение пользовательского типа
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ