Перейти к содержанию

Переменные и HTTP-запрос

Документ запроса уже выбирает поля и различает два курса псевдонимами. Теперь передадим значения через переменные и обернём выполнение в HTTP. Сервер Академии будет принимать POST по /graphql с JSON-конвертом. Важное различие: GraphQL определяет схему и выполнение операции, а наш сервер устанавливает конкретный транспортный договор, ограничения тела и форму ответа на неправильный JSON.

Сервер слушает только 127.0.0.1:4494. Это локальный учебный адрес. Пока никакие удалённые базы, токены или публичные сервисы не подключены. После самостоятельного запуска приложение хранит вымышленные данные в памяти процесса. Перезапуск возвращает исходный набор. В этом уроке ещё нет страницы клиента: читатель может изучить конверт или отправить запрос самостоятельно из подходящего инструмента.

Тип переменной и значение

Операция query One($id: ID!) объявляет переменную. Внутри выборки аргумент использует $id, а отдельный объект variables содержит значение. Это позволяет сохранить один документ для разных карточек. Строка query не переписывается при выборе другого курса. Граница похожа по смыслу на параметризацию запроса к хранилищу, но относится к другому языку: сначала проверяется договор GraphQL, а способ чтения данных определяет resolver.

Нельзя считать любое значение variables допустимым только потому, что JSON разобрался. GraphQL проверяет соответствие объявленному типу и обязательность. Отсутствующий id или null не соответствует ID!, поэтому операция не должна начать нормальное выполнение. Тип ID предназначен для идентификатора, а не для арифметики. В нашем предметном наборе используются строки c01 и c02; клиент должен сохранять их значение, а не пытаться вычислять следующий ID.

{
  "query": "query One($id:ID!){course(id:$id){id title}}",
  "variables": {
    "id": "c01"
  },
  "operationName": "One"
}

Это полный конверт тела запроса к текущему серверу. operationName указывает конкретную именованную операцию. Внутри query находится язык GraphQL, снаружи — JSON. Ошибка кавычек внешнего JSON обнаруживается транспортом, а неизвестное поле внутри query — проверкой схемы. Такие причины могут проявляться похожим сообщением клиента, но исправлять нужно разные слои и разные файлы.

Ограниченный POST

Node-сервер принимает только POST к точному пути /graphql и ожидает Content-Type application/json. Другой метод или путь не становится альтернативным способом чтения. В коде нет выполнения mutation через GET. Учебный договор намеренно небольшой; он не объявляется полной реализацией всех вариантов GraphQL over HTTP. Клиент должен следовать именно тем правилам, которые показаны в исходниках состояния lesson-03.

Тело читается частями с подсчётом байтов. Предел составляет 16384 байта. Если он превышен, сервер возвращает 413 до разбора JSON. Проверка длины строки query дополнительно ограничивает документ, но не заменяет предел тела: variables тоже занимают память. Сначала ограничиваем транспортный ресурс, затем форму конверта, затем язык запроса. Каждая граница уменьшает собственный вид неопределённости.

Конверт должен быть объектом, а query — строкой. Массив вместо конверта отклоняется: пакетные HTTP-запросы в этом курсе не реализованы. Variables при наличии должны быть объектом, а operationName — строкой. Нельзя позволять произвольному значению добраться до функции языка и надеяться, что любая ошибка там даст удобный отказ. Простая проверка формы входа сохраняет ясный договор и уменьшает вероятность случайного внутреннего исключения.

Статусы транспорта и ошибки языка

Неправильный JSON получает 400. Несогласованный Content-Type получает 415. Слишком большое тело — 413. Эти ответы не содержат обычного GraphQL data, потому что сервер ещё не получил допустимый конверт операции. Когда конверт принят, результат GraphQL возвращается JSON-ответом со статусом 200, даже если содержит errors проверки или исполнения. Это выбранный договор демонстрации; клиент обязан читать не только HTTP-статус.

Такое упрощение следует отличать от строгого промышленного транспортного профиля с отдельным медиатипом и правилами статусов. Курс не обещает полную спецификационную поддержку каждой комбинации. Его цель — показать понятный путь одного запроса. При развитии публичного API нужно выбрать поддерживаемый профиль, документировать тип ответа и согласовать обработку ошибок клиентом. Без этого два корректных по отдельности компонента могут неправильно понимать результат друг друга.

Сервер отвергает Origin, если он присутствует и отличается от локального адреса. Это ограничивает обращения из другой браузерной страницы, но не является проверкой личности и не запрещает произвольному HTTP-клиенту отправить запрос. На данном шаге нет входа. Позже введём учебный субъект в контексте, сохранив чёткую границу между происхождением запроса, форматом транспорта и разрешением предметного действия.

Чтение ожидаемого результата

Для id c01 ожидается data.course с идентификатором и названием «Статический сайт». Для неизвестного ID — data.course:null. Если обязательная переменная отсутствует, ответ языка содержит errors, а обычное чтение не выполняется. Если внешний JSON обрезан, отказ происходит раньше. Эти четыре сценария полезно читать последовательно: одинаковый endpoint не означает одинаковую причину отсутствия карточки.

В архиве есть полный academy.mjs. Он ограничивает время заголовков и обращения, сохраняет короткую внутреннюю ошибку для клиента, а подробное исключение выводит в терминал. Учебный процесс не запускался при подготовке, поэтому примеры описывают ожидаемую выдачу по исходникам. Установка зависимости и команда старта находятся в README для самостоятельного прохождения. Никакого полученного сетевого результата или успешной проверки среды статья не заявляет.

Попробуйте мысленно поменять только variables, оставив query прежней. Выборка и проверенный договор типов сохраняются, а предметный объект меняется. Такой подход упрощает клиент и позволяет хранить известные документы отдельно от пользовательских значений. В следующем уроке добавим вложенного автора: теперь серверу придётся связать один объект курса с другим источником, не раскрывая внутренний authorId как обязательную деталь каждого клиента.

Строка HTTP-пути здесь сравнивается точно, включая отсутствие дополнительных параметров. Поэтому запрос к /graphql?debug=1 не является тем же маршрутом в учебной реализации. В большом сервере обычно разбирают URL отдельно и явно определяют разрешённые параметры. Наша точная проверка сохраняет узкий договор и не вводит скрытый режим отладки. Если читатель добавляет такой режим самостоятельно, он должен решить, какие сведения можно вернуть и почему клиенту доступны дополнительные данные. Аналогично один локальный адрес не означает автоматическую поддержку HTTPS: защищённый транспорт потребовал бы отдельной конфигурации, а не только смены текста ссылки в README.

Исходники урока

Оглавление серии · Скачать исходники. Полное самостоятельное состояние находится в lesson-03. Учебные приложения, зависимости, база и проверки при подготовке не запускались; описанный результат ожидается по исходникам. Материалы пока находятся в локальной редакционной версии.

Первичные источники: документация 1.