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. Ваш наступний крок — навчитися будувати ще більш складні запити, додавати фільтрацію та інтегрувати автентифікацію. Прямо як у знаменитій книзі "Гаррі Поттер і методи раціонального програмування" — чим більше ви практикуєтеся, тим кращим стає ваш код!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ