Схема и SDL: контракт между клиентом и сервером
Схема — это формальное описание всего, что можно спросить у API. Её пишут на языке SDL, и она служит строгим контрактом между клиентом и сервером.
Нет схемы — нет GraphQL. Схема — это и документация, и контракт, и валидатор одновременно. С неё начинается любой сервер.
В REST контракт API живёт где-то отдельно: в Swagger-файле, в вики, в голове бэкендера. В GraphQL контракт — это сама схема, и она часть рантайма. Сервер физически не примет запрос, который не описан в схеме. Поэтому схема не врёт: если поле есть в схеме — оно гарантированно доступно, если его нет — запрос будет отклонён ещё до резолверов.
Пишут схему на SDL — Schema Definition Language. Это компактный декларативный синтаксис: ты описываешь типы и их поля, а сервер сам строит из этого исполняемую схему. У SDL есть приятное свойство — он язык-независимый. Одну и ту же схему можно реализовать на сервере хоть на Node.js с Apollo, хоть на Python, хоть на Go или Java: SDL описывает контракт, а не реализацию. Благодаря этому фронтенд и бэкенд могут договариваться о схеме заранее, ещё до написания кода, и работать параллельно — фронт пишет запросы под согласованную схему, бэк наполняет её резолверами. Такой «schema-first» подход — одна из причин, почему GraphQL хорошо ложится на командную разработку: схема становится единым источником правды, который видят и люди, и инструменты.
Минимальная схема
type User {
id: ID!
name: String!
age: Int
}
type Query {
user(id: ID!): User
users: [User!]!
}
Читается так: есть тип User с полями id, name, age. Есть особый тип Query — корень для всех запросов на чтение. В нём два поля: user принимает аргумент id и возвращает одного пользователя, users возвращает список пользователей. Восклицательные знаки и квадратные скобки — модификаторы типов, их разберём в отдельном уроке.
Граф типов
Название «GraphQL» не случайно: схема — это граф. Типы — узлы, поля-ссылки на другие типы — рёбра. Вот как выглядит небольшая схема блога:
+------------------+
| Query | (корень чтения)
+------------------+
| user | posts
v v
+--------+ +--------+
| User |<---| Post |
+--------+ +--------+
| posts | author (ссылка назад на User)
v | comments
[Post!]! v
[Comment!]!
Клиент, по сути, путешествует по этому графу: входит через поле Query, переходит по рёбрам (полям-ссылкам) и собирает нужное поддерево.
Как работает под капотом
При старте сервер парсит SDL-строку и строит объект схемы: словарь типов, у каждого — словарь полей, у каждого поля — его тип и аргументы. Когда приходит запрос, валидатор пробегает по нему и сверяет каждое поле с этим словарём. Несуществующее поле или неверный тип аргумента — мгновенный отказ, резолверы даже не запускаются.
Смоделируем «словарь типов» и валидацию запроса на JS:
const schema = {
User: { fields: ["id", "name", "age"] }
};
function validate(typeName, requestedFields) {
const known = schema[typeName].fields;
const bad = requestedFields.filter(f => !known.includes(f));
return bad.length ? "Ошибка: нет полей " + bad.join(", ") : "OK";
}
console.log(validate("User", ["id", "name"])); // OK
console.log(validate("User", ["id", "salary"])); // нет полей salary
Попробуй сам ▶ — попроси поле, которого нет в схеме, и увидишь, как валидатор его ловит. Ровно это делает GraphQL-сервер до выполнения запроса.
Частые ошибки
- Забыть тип Query. Без корневого типа
Queryсхема невалидна — спрашивать будет нечего. - Путать схему и данные. Схема описывает форму и возможности, но не содержит самих данных — их отдают резолверы.
- Менять схему ломающе. Удалить поле или сделать nullable-поле обязательным — это breaking change для всех клиентов. О безопасной эволюции будет отдельный разговор.
Best practices
- Проектируй схему «от клиента»: какие экраны и сценарии есть в приложении — такие поля и связи и нужны. Схема описывает домен, а не таблицы БД.
- Держи схему в отдельных
.graphql-файлах под контролем версий — её удобно ревьюить как обычный код. - Имена типов — с большой буквы и в единственном числе (
User, неusers); имена полей — camelCase.
Итоги
Схема на языке SDL — это сердце GraphQL: строгий контракт, документация и валидатор в одном лице. Она описывает типы (узлы) и поля-связи (рёбра), образуя граф данных, по которому путешествует клиент. Корнем чтения служит тип Query. Дальше разберём строительные блоки типов — скаляры, объекты и модификаторы.