Pull to refresh

Comments 90

Спасибо за статью.
Будут ли части про «Persisted Queries», ошибки, пагинацию, аутентификацию?
Спасибо за доброе слово) Да, это будет во второй части. Контент уже есть, предполагаю что будет в ближайшие дни.
UFO just landed and posted this here
Спасибо, тут конечно возможно не совсем корректная фраза, в плане формулировки. Тут правильно говорить не «основан», а «базируется». Фактически т.к REST — это лишь архитектурный стиль по этому фактически может использоваться разный протокол, SNMP, SMTP и другие, но все же REST имеет ограничения.
UFO just landed and posted this here

Скорее наоборот, HTTP базируется на принципах REST.

Вот уж воистину все новое — это хорошо забытое старое)
пысы. зря вас заминусовали

Собственно принципы REST были сформулированы Роем Филдингом в 2000-м году и были использованы при работе над спекой HTTP 1.1. Наверное, минусуют за то, что конкретную версию HTTP не указал :)

Самое забавное, что REST, скорее всего, никуда не делся, он остался на backend (на рисунке Service) и свой трафик все равно отправляет. Т.е. с клиента запрашиваем одно поля, graphQL получает от сервиса все поля и отфильтровывает одно. Интересно насколько такое решение сажает производительность в сложных запросах.
UFO just landed and posted this here
Верно, но взамен получаем задержку в запросе на обработку данных graphQL сервером и потенциально бутылочное горлышко в виде единой точки входа. Еще вопрос, насколько легко масштабируются такие graphQL сервера
Задержка на агрегацию данных является минимальной даже при холодном старте, далее данные достаются из кэша. Про проблеме в единой точке входе я так понимаю имеется в виду нагрузка на сервер? «Еще вопрос, насколько легко масштабируются такие graphQL сервера» — одним из основных пойнтов как раз является простота масштабирования GraphQL API. Возможно как расширять существующие типы так и описывать новые.
Вопрос не про агрегацию, а про обработку. Т.е. graphQL сервер должен принять запрос, валидировать его, разобрать, сформировать запрос к сервису, получить ответы от всех сервисов, собрать ответ клиенту. На это требуется время, которое плюсуется к времени ответа от сервиса. Вот я о чем. Кэш отчасти спасает ситуацию, но иногда данные меняются часто. Ну и тут же опять вопрос кто будет поддерживать кеш graphQL сервера актуальным (обновлять)?

Если агрегации данных от нескольких сервисов нет, то можно не говорить про graphQL-сервер — запрос к сервису это обычный вызов репозитория или что-то там у вас дергают rest-контроллеры или их аналоги. По сути, можно считать graphQL-слой очень умным фильтром-трансформером в цепочке типа response = Json.serialize(graphQL.filter(userRepository.getAll(), request));

Не обязательно. В некоторых компаниях между сервисами общение происходит по RPC, отличному от http/rest. А их, помимо grpc, великое множество. Просадка имеется не в производительности, а в latency. И это на больших масштабах вполне приемлемая цена за масштабируемость.
Не обязательно, поэтому и написал «скорее всего». Я описывал тот кейс, когда до graphQL уже были работающие сервисы

Рисунок довольно схематический, graphQL сервер отдельный совсем не обязателен, а апп-сервер (бэкенд) может сам отдавать graphQL, обеспечивая, например, прямой маппинг на SQL.

GraphQL использует систему типов для описания данных

Нормальные люди используют sdl для декларации схемы =)


schema {
   query: Query
}

type Query {
    users: [User!]!
}

type User {
    name: String!
    email: String!
    age: Int!
    friends: [User!]!
}
Фреймворк, который построен по принципу Graphcool sdl деклараций схем GraphQL, в репозитарии все инструменты, очень круто.
1) Это не фрейм, а SAAS/BAAS решение, которое в реальности мало кому нужно.
2) Это не «по принципу», а наоборот, ребята, которые внедрили SDL в GraphQL, т.е. первоисточник этих схем.
Ну может я чего не знаю…

Можете, пожалуйста, кинуть ссылку на установку этого фрейма? Я вот, например хочу скачать, поставить, настроить роутинг, настроить резолверы и проч. А не пользоваться их веб мордой. ;)
См. здесь, там расписано что делать, приведены примеры реализации для React, Vue и тд, есть даже IDE со всякими плюшками Playground, необязательно деплоить к ним на сервер, можно запускать локальные версии
Видимо я ошибался. Я лишь поверхностно проглядывал туториалы и брал «на поиграться», но везде и всюду предлагалось использовать их как BAAS решение, ни слова про докер в мануалах.

Спасибо! Буду теперь знать.
Не за что, я сам в процессе разработки и долго выискивал оптимальное решение для GraphQL, залез в исходники graphcool и увидел все что искал :)
И кстати блог у них полезно читать, там все по полкам разложено Server basics
Это всё же экспериментная фича, которая лишь 4 дня назад сдвинулась с мёртвой точки (мой любимый pull/90). А до этого её никто не трогал два года.

Так что я бы сказал не «нормальные люди», а те, кому просто деваться некуда. Насколько я помню, реализация SDL парсеров/компилеров есть лишь на JS, PHP и Ruby причём у каждой свои фичи и/или недоработки.

1) JS: github.com/graphql/graphql-js
2) PHP: github.com/railt/railt
3) Ruby: github.com/rmosolgo/graphql-ruby

UPD: Посмотрел, там свой DSL, а не нативная схема, так что только два варианта. Может кто ещё знает что на других языках?
Возможность формировать структуру и объем данных на клиенте

Я правильно, что на практике сервер всё равно поддерживает предопределённые запросы и наборы полей под конкретные кейсы? Если да, то в чём тут преимущество?
Я правильно, что на практике сервер всё равно поддерживает предопределённые запросы и наборы полей под конкретные кейсы?

Не факт, зависит от реализации сервера.

Если да, то в чём тут преимущество?

Стандартизация критериев выборки. Использование:
query {
    user {
        id,
        login,
        avatar,
        friends(latest: 10) { id, login }
    }
}


Вместо, например:
/api/1.0/users?fields=id,login,avatar&with=friends.id,friends.login&count=friends:10


Или нечта подобного.
А какие варианты реализации этого на сервере? Просто зачастую то, в каком виде данные хранятся и то, в каком виде они возвращаются, совершенно разные вещи. Поэтому непонятно, как реализовать такой универсальный обработчик. Ведь в зависимости от того, какие поля нужны, приходится сильно варьировать запрос, чтобы не убить производительность. Плюс если какие-то поля вернуть в списке — не проблема, то другие вычислять для каждого элемента — дорого и имеет смысл только при запросе деталей одного элемента

Типичный вариант до оптимизации быстродействия — из репозитория ORM вытягивается нужный граф (желательно с lazy) и фильтруется по запросу.

Ведь в зависимости от того, какие поля нужны, приходится сильно варьировать запрос, чтобы не убить производительность.

Архитектурно неправильно графом ходить в хранилище данных. Граф является интерфейсом (точнее поля типа данных, для которого строится схема графа) к репозиторию. Каждый тип данных (пользователь, статья и т.п.) — это репозитории, к которым обращается граф. Как эти репозитории получают данные, как они их агрегируют, кешируют — это их детали реализации.

Рассмотрите абсолютно любую бизнес-модель с позиции, что у вас есть универсальный апи-метод, позволяющий получить каждое из полей этой модели. Как это поле будет собираться — это дело самой бизнес-модели. Можно анализировать граф-запрос, но сама логика графа подразумевает, что запрашиваться будет самый разный набор полей, поэтому чаще всего эффективнее кешировать либо всю модель, либо поля модели.

При использовании графа ответственность смещается с SLA на конкретный эндпоинт (с соответствующими уровнями оптимизации запросов) на SLA доступа ко всем предоставляемым данным. Решается задача не оптимизации медленных запросов к данным, а как отдавать данные вне зависимости от того, какие там могут быть запросы.
Решается задача не оптимизации медленных запросов к данным, а как отдавать данные вне зависимости от того, какие там могут быть запросы.

Я так понял, вопрос был в том, как реализовать такой механизм и не убить производительность. Очень сложно сделать реализацию быстрой выборки чего-угодно. Вместо этого делают фиксированный набор полей, на выборку которого затачивают запрос и т.д.
Вместо этого делают фиксированный набор полей, на выборку которого затачивают запрос и т.д.

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

В конце концов такие модели с тяжелой логикой получения данных обзаводятся слоем кеша со сквозной записью.

Если самим графом за данными не ходить, четко разделяя уровни ответственности, то этой проблемы просто нет как таковой.

Никак. Поэтому на самом деле мало кто пользуется graphql и мало кто пытается. Потому что все сводится обычно или к потери производительности, или к тому, что оборачивают rest api в этот ваш graphql. Куча компаний не умеют делать даже что-то похожее на согласованный REST, а тут уже graphQL. Не к добру это.


Ну и к тем, кто предлагает просто смапить orm с graphQL. Так обычно не работает, потому что нужна еще куча внутренних проверок, например, имеет ли пользователь доступ к этой записи. И их может быть довольно много.

Куча компаний не умеют делать даже что-то похожее на согласованный REST, а тут уже graphQL

Проблема "согласованного REST" прежде всего в том, что REST — это архитектурный принцип, который каждый толкует как хочет даже в теории, не говоря о конкретной имплементации. graphQL же чётко описанный стандарт с референсной имплементацией.


Так обычно не работает, потому что нужна еще куча внутренних проверок, например, имеет ли пользователь доступ к этой записи.

Решаемо, на разных уровнях и задачах по разному. Никто не мешает, например, в резолвер заинжектить сервис авторизации и проверять хоть запросы к хранилищу, хоть ответы.

Проблема "согласованного REST" прежде всего в том, что REST — это архитектурный принцип, который каждый толкует как хочет даже в теории, не говоря о конкретной имплементации. graphQL же чётко описанный стандарт с референсной имплементацией.

А вы знаете в чем проблема "одного единого стандарта"? Ну и да, странно звучит, вот ребята используют какой-то sdl, а в стандарте его нет. GraphQL это тоже идея, все-таки. А структуру все все равно будут делать по своему.


Решаемо, на разных уровнях и задачах по разному. Никто не мешает, например, в резолвер заинжектить сервис авторизации и проверять хоть запросы к хранилищу, хоть ответы.

Я не говорю, что это невозможно, но это означает, что вам надо будет это делать. Инжектить сервис авторизации, потом еще инжектить сервис для проверки ip-whitelist, а потом еще помнить для какой модели что использовать, а что нет. Тут просто уйма веселый архитектурных проблем, которые позволял избегать REST.

А вы знаете в чем проблема «одного единого стандарта»? Ну и да, странно звучит, вот ребята используют какой-то sdl, а в стандарте его нет. GraphQL это тоже идея, все-таки. А структуру все все равно будут делать по своему.


sdl это какой-то инструмент сбоку для описания схемы, у меня в Эликсире там тоже свой дсл, но для клиента это будет тот же самый GraphQL, по стандарту. Это не идея, можно пойти и скачать спецификацию. Спецификации REST в природе не существует.
А структуру все все равно будут делать по своему.

Язык запроса стандартизирован, так сказать, де-юре, а передача запросов и ответов по HTTP де-факто (а может и де-юре тоже, не вникал).


Тут просто уйма веселый архитектурных проблем, которые позволял избегать REST.

GraphQL легко сводится к типичным JSON REST(ish) API c "бесплатным бонусом" в виде возможности получать не все данные, которые может сервер отдать, а только необходимые. Если у вас в архитектуре есть слой, который отвечает за выборку данных из хранилища с учётом прав пользователя, его IP, и прочих метаданных запроса, сессии и т. д., то GraphQL ендпоинт будет лишь ещё одним интерфейсом к нему.

Http де-факто, как самый популярный транспорт. В стандарте ничего специфичного для него нет, да и разработчики заявляли, что GraphQL is protocol agnostic.
Когда используется схема «клиент -> запрашивает данные через граф -> который, опираясь на описание типов, запрашивает нужные данные в нужных репозиториях -> которые, основываясь своей бизнес-логике, запрашивают данные из своих хранилищ», то вопросов, для чего нужен граф и какие задачи он решает, не возникает.

Почти всем нерешаемые проблемы графа связаны с тем, что его пытаются применить не по назначению. Возьмем, например, пагинацию.
Есть ОРМ, которая поддерживает работу с графом. Прикручиваем пагинатор к ОРМ и через этот пагинатор ходим напрямую в ОРМ. И имеем все те проблемы, которые описывают в каждой второй статье применения графа.

Как это решается через вышеописанную схему?
Создается тип пагинированных данных. Интерфейс репозиториев расширяется, чтобы они умели отдавать данные согласно новому типу данных. Как результат пагинатором ОРМ пользуются репозитории. А граф запрашивает у репозиториев данные соответствующего типа. Задача получения данных и оптимизации запросов остается на уровне модели, которую использует репозиторий для работы с хранилищем.
Я правильно, что на практике сервер всё равно поддерживает предопределённые запросы и наборы полей под конкретные кейсы? Если да, то в чём тут преимущество?

Я выделил бы два ключевых преимущетва.

У нас есть универсальный геттер для данных. На уровне описания типов бизнес-моделей описываются их поля и связи (поля с типами других моделей). На основе этих типов потом строится вся схема графа. Бонусом можно на основе внутренней логики регулировать, какие типы кто будет видеть. Если в описании схемы какого-то запрошенного поля нет (вырезано из-за отсутствия прав), то запрос с таким полем не пройдет сам по себе. Логика резолва всех указанных в графе полей спускается на уровень ниже, для которого декларируемые поля являются интерфейсом, согласно которому сервис должен уметь возвращать данные

Второе преимущество в сеттерах (мутации). С одной стороны для каждой операции бизнес-логики пишется своя мутация (как бы аналог эндпоинта в рест), но из-за вышеописанного преимущества с декларативным способом описания, методы бизнес-логики превращаются в мутации, где наборы полей описываются таким же декларативным методом. А сама бизнес-логика уходит на следующий уровень, для которого мутация становится просто интерфейсом.

В итоге поддержка апи сводится к декларации получаемых данных и декларации методов изменения данных. Сама реализация же может делаться где угодно и как угодно, пока она использует описанные в графе декларации в качестве интерфейса.

На практике часто (можно даже сказать по умолчанию для внутренних API) клиент может выбрать весь граф объектов, которы может собрать бизнес-логика в принципе.

Не соглашусь, зачастую набор данных для view существенно меньше чем может вернуть API.
Самый банальный пример опять таки с пользователем. Допустим у нас залогиненый пользователь и мы должны отображать его имя и аватарку в хеде страницы. В таком случаи нам нужно всего 2-3 поля, а не данные по всему интерфейсу пользователя. К тому же можно увеличить TTI за счёт того что первым запросом запрашивать данные для видимой пользователем части экрана, а остальное подгружать лениво.

Фишка GraphQL в том, что во вью (ответе сервера) всегда ровно столько данных, сколько нужно клиенту (фронту). Нужны только имя и аватарка — выбираете только их, нужны паспортные данные друзей друзей друзей — выбираете их. У вас неограниченная (логически в теории, на практике разработчики бэкенда могут ограничивать по соображениям экономии ресурсов и безопасности, да и сами вы можете не захотеть ждать результатов "семиэтажного" запроса, который выполняется полчаса) возможность обходить предоставленную через GraphQL модель.

Посмотрите на проблему с точки зрения клиента, в особенности – мобильного, на слабом канале, когда очень желательно получать минимум требуемых данных, без лишнего «мусора». Отсюда и «шейпинг» возвращаемых данных клиентом при запросе. В случае решения а-ля REST (т.к. это уже ни разу не REST, а JSON RPC какой-то) приходится или плодить кучу эндпоинтов на каждый чих, либо снабжать ресурс кучей get-параметров, определяющих результат, что, по-факту, и есть переизобретение GraphQL. Да, можно сказать, что возможные запросы предопределены, но в зависимости от набора полей в запросе, нормальный бекенд будет делать разные запросы в хранилище. Банальный пример – не делать лишний запрос или join для вложенной коллекции модели, если ее не просили.
С каких пор оборачивание SQL запросов в HTTP перестало быть моветоном?
Не совсем понимаю где вы увидели оборачивание SQL запросов в HTTP?
Ну там же целых две буквы совпадают в названиях, как же это можно не заметить? =)

</irony>
Вот вопрос, стоит ли GraphQL того, чтобы бросать все и внедрять его, если и без него все работает идеально??? (Не только в плане производительности, но и клиент-серверной коммуникации в целом)

А в плане разработки? Как часто ваши фронтендеры просят бэкендеров добавить в API то или иное (уже существующее в бэкенд-модели поле или целый граф), а то и целый новый ендпоинт, выдающий те же данные, что и другие, но в другом шейпе?


Для проектов, не испытывающих особой нужды в низкоуровневых плюшках GraphQL, могут оказаться полезными архитектурные, снижающие зависимость фронтов от бэкенда.

GraphQL без сомнения очень интересная технология, но большой вопрос как для него строить секьюрити, если я могу с помощью REST разбить получение, к примеру полных деталей пользователя и сокращенных (без sensitive data), просто создав два REST endpoint с разными пермиссиями на них, то как это сделать с QraphQL?
Это делается на уровне генерации типа данных для схемы. Если в схеме нет какого-то типа или поля в типе, то их запрос приведет к общей ошибке обработки запроса (запрос несуществующего типа или несуществующего поля).

В общем случае это решается наличием соответствующего функционала у репозитория. Тип возвращаемых данных — это один из его интерфейсов. Используя этот тип данных репозиторий преобразует внутреннее хранение полученных из хранилища данных в стукруту, которая передается пользователю. Аналогичным образом граф у репозитория получает этот тип данных в соответствующем формате.

Ограничение доступа остаётся на уровне репозитория, который сам фильтрует, по своей внутренней логике, какие поля кому доступны. Что клиент не может получить у этого репозитория недоступные ему поля (потому что всякая выдача проходит через преобразование в этот тип данных), что граф, запрашивая схему для типа данных, получает только тот набор полей, который доступен данному пользователю.

Добавлю, что вы можете создать два типа данных (запроса), например, UserPublic и UserPrivate, что будет аналогом двух ендпоинтов. Или в резолвере одного типа User проверять права и отдавать специальные значения для полей, к которым доступа нет.

Добавлю, что вы можете создать два типа данных (запроса), например, UserPublic и UserPrivate, что будет аналогом двух ендпоинтов

Это нарушает саму идеологию графа. Запросить пользователя мы можем в любом месте любого запроса, который имеет связь с пользователем: { article { user { id } } }
Если мы будем использовать разные названия типов в зависимости от прав, то убъем универсальность доступа к данным.

Тем не менее это рабочее решение.

Это и сразу выглядит как костыль в рамках GraphQL напрочь убивающее большую часть его пользы.

А уж в рамках одного из комментариев выше
В случае решения а-ля REST (т.к. это уже ни разу не REST, а JSON RPC какой-то) приходится или плодить кучу эндпоинтов на каждый чих, либо снабжать ресурс кучей get-параметров

Сам GraphQL выглядит как банальное «not invented here»
Если всё упростить, то получиться, что у вас есть Map<String, Object> и запрос по получению данных из неё по ключу. Т.е. вы можете просто не заполнять эти поля если нету соответствующих прав.

Видимо пермишны на атрибуты вешать и в fine-grained авторизацию уходить.

Вообще не проблема или я не понял вопроса (-:
Если у пользователя есть пермишины — он получает полные данные, скажем так, выполняется один запрос, если нету, то другой. Это если вы хотите решать на клиенте.
Если Вы решите это выяснять на сервере, то просто сами решаете, что возвращать пользователю в зависимости от его уровня доступа, так сказать.
Сколько ни читаю посты об GraphQL, не могу понять чем он принципиально лучше существующих уже давным давно URI-совместимых query language-ей. Например, RQL или odata. Они прекрасно решают проблему выбора необходимых полей, поддерживают фильтрацию, паджинацию и все остальное. В отличии от GraphQL они не противопоставляют себя REST-у, а отлично его дополняют. Таким образом, сравнивать GraphQL нужно не с голым REST, а с существующими языками запросов.
Система типов для описания данных, в свою очередь, очень похожа на также давно существующую JSON Schema.

Если говорить о REST как типичных HTTP RESTish API, то они идеологически плохо поддерживают не CRUD операции над ресурсами. Приходится придумывать виртуальные ресурсы типа /process/123/approve для расширения списка доступных действий над ресурсом, когда и ресурса собственно нет.

UFO just landed and posted this here

Идентификатор (URI) /process/123/approve, а по факту в модели области нет даже сущности Process, не говоря о её методе approve, только идентификатор для связи сущностей. В СУБД просто sequence без таблицы, а можно генератор айдишников сделать вообще базу не трогая.


Нет ресурса, который можно посмотреть по GET /process/123, близкое только GET /process/123/status, возвращающй коллекцию статусов связанных сущностей. Нет ничего, что можно отправить PUT /process/123 или PUT /process/123/status, только POST /process/123/, где verb — один из полутора десятка глаголов.

UFO just landed and posted this here

Хороший пример того, что мне не нравится в типичном REST — введение дополнительных абстракций (в примере — виртуального идентификатора и подресурса, который не ресурс) для… даже не знаю для чего. Чтобы соотвествовать семантике HTTP? Но вроде даже она не вводит понятия подресурсов.

Количество ресурсов, их очень много.
Сколько ни читаю посты об GraphQL, не могу понять чем он принципиально лучше существующих уже давным давно URI-совместимых query language-ей. Например, RQL или odata. Они прекрасно решают проблему выбора необходимых полей, поддерживают фильтрацию, паджинацию и все остальное. В отличии от GraphQL они не противопоставляют себя REST-у, а отлично его дополняют.

Киллер-фича графа — отсутствие необходимости версионировать апи. Из чего вытекает простота использования и поддержки точки взаимодействия клиента и приложения.

Граф противопоставляется ресту, потому что идеологически граф не общается с хранилищем, а только с репозиториями. Рест-апи в своем развитии обзаводится особыми эндпоинтами, которые получают сложные наборы данных и обзаводятся слоем логики, позволяющем получить эти данные из хранилищ наиболее оптимальным способом. И очень часто при внедрении графа возникает именно вопрос о том, как это повторить для графа. Никак. Граф не должен знать ничего о том, как хранятся данные. Он должен знать только то, какой севрис обслуживает какие типы данных.
Киллер-фича графа — отсутствие необходимости версионировать апи. Из чего вытекает простота использования и поддержки точки взаимодействия клиента и приложения.

А почему вы считаете, что у других языков запросов есть потребность в версионировании?

Внезапно в данный как раз занимаюсь одним бизнес приложением с использованием этой технологии и могу много чего интересного рассказать.


На бэке стоит внезапно php и laravel для подключения используется единственная более-менее адекватный коннектор который по сути обертка над этой либой


На фронте используется React + Apollo


И так на backend находятся 25 таблиц в postgres причем 4 из них содержат jsonb поля с произвольным содержанием.


А теперь головные боли:
1) backend


Для того чтобы graphql заработал нужно описать запросы типы и мутации.


В коробке с graphql идет целая куча примитивных типов — вроде числа, строки, boolean и т.д
Далее идут структурные типы вроде объектов — причем для описания своего объекта необходимо описать ВСЕ поля объекта на основе существующих полей. в итоге 25 объектов в среднем с 8-10 полями — это ОЧЕНЬ много описаний. Но это еще не все внезапно нет готового типа для произвольного json — типы должны быть точно описаны. Но добавить свой тип в принципе возможно — пришлось добавить тип json самостоятельно. Связи указываются как поле которое ссылается на другой тип.


Хорошо у нас есть типы объектов но сами по себе они бесполезны! необходимы запросы к объектам на основе эти типов. предположим я желаю 2 типа запросов на каждый объект первый — показать по id или по другому ключевому полю и второй — показать все по каком-либо набору критерию. В итоге мы уже описываем аргументы. В итоге по 2-5 аргументов на каждую сущность в каждом запросе по два на сущность — и это опять достаточно много описаний.


Далее мутации — если взять 3 мутации (удалить, добавить, изменить) то опять описываем аргументы, для мутаций уже те самые 8-10 полей и это опять куча аргументов + еще правила валидации для каждого поля в каждой мутации.


Смотрим видим что необходимо создать просто огромное количество кода для описания graphql для CRUD+list для 25 объектов.


В итоге рвем на голове волосы от чисто объема кода и пишем некий генератор который используя рефлексию и немного doctrine/dbal шерстит наши таблицы и генерирует поля для наших типов и аргументы для наших запросов и мутаций. Сам генератор принимает список объектов и какие поля/аргументы не надо генерировать.


Все это месяц работы.


2) фронт


Здесь нам опять необходимо описать наши 50 запросов и 150 мутаций — хорошо есть генератор из php который выдаст нам схему и мы определим хитрый Higher-Order Component (HOC) который будет принимать конфиг из серии


Объект


  • поля
  • запросы
    • аргументы запроса
    • поля запроса
  • мутации
    • аргументы мутации
    • поля мутации

После чего генерирует код наших запросов/мутаций (graphql-tag), и прописывает их в props нашего объекта.


Хорошо — теперь бы нам пробросить наши запросы в props наших объектов.
Для этого внезапно Apollo предлагает свои HOC который принимают graphql-tag и пробрасывают свойства в объект. И для контроля выполнения запросов еще один HOC.


В итоге получаем эдакий толстый бургер где наши 2 HOC это 2 булки — один для предоставления и генерации запросов, второй для того чтобы упростить вызов этих запросов а между ними уже лежит начинка в виде HOC для каждого запроса/мутации.


После чего строим 25 data-table оборачиваем их в наш HOC с разными конфигами и радуемся.


Все это звучит понятно просто и легко но по факту это достаточно сложная работа.


Итог:


  • Если у вас более 5 объектов в системе и вы не хотите её делать год одному и 3 месяца одной командой из 5 человек — не используйте graphql.
  • если вы не используете специальный saas/baas или другой софт который использует graphql из коробки и умеет генерировать запросы/мутации на основе типов — не используйте graphql.
  • если у вас не graphqlCool фреймворк например meteor не используйте graphql

А чем бы вам помог REST? Если у вас 25 таблиц, и к каждой нужен крад, то получается вам надо написать 125 эндпоинтов, и все это с фильтрацией, валидацией, и прочим. Не думаю что это заняло бы меньше времени.

Все, конечно, зависит от выбранного стека технологий, но под REST уже много удобных инструментов появилось. Например, в мире Java есть Spring Data Rest, с помощью которого для реализации кейса с 25 таблицами не нужно писать ни одного endpoint руками. Достаточно описать 25 сущностей в виде название сущности, список полей с типами и собственно все. Таблицы в БД и эндпоинты (CRUD) будут сделаны автоматически, с пейджингом и сортировкой

Собственно мне и пришлось реализовать подобные генераторы для graphql с костылями в велосипедами ибо ручками такой объем кода не потяну физически.

1) Я не говорю что сам протокол graphql плох
2) Имхо для описание одного REST ресурса потребуется чуть меньше времени чем на описание 1 типа 3 мутаций и 2 запросов и единственный два плюса в graphql против REST это возможность выбрать список полей (включая условные выборки) и простая выборка полей связанных объектов — собственно для чего мне он и нужен — из 25 объектов 22 можно вытянуть по зависимостям имея на руках любой из них.
3) Для использования graphql нужен отдельный сервер relay/meteor а для бэка остальных платформ кроме node.js остается только использовать Apollo клиент а эти ребята полны сюрпризов я скажу — например могут внезапно выпилить поддержку Redux в новой версии и одновременно закрыть доки на старую, плюс не допилить доки на новую в итоге документацию приходиться вытаскивать их кэша гугла.
4) Для REST есть куча готовых генераторов которые позволят автоматизировать занудное создание ресурсов а для graphql мне пришлось писать такой генератор самому — причем аж 2 раза — первый для сервера а второй для клиента.

Для использования graphql нужен отдельный сервер relay/meteor

Зачем?

Redux уже вне Apollo. И это неплохо.
У меня Apollo на фронте и на сервере. Получаю просто удавольствие от GraphQL.
Всё пишется просто, комфортно.

И вообще не понял, причём тут graphqlCool к meteor. От слова совсем.
А теперь головные боли:

Добавьте ещё головную боль в виде размазанной бизнес-логики по мутациям / орм с кучей дублируемого кода, для чего пришлось использовать написать генераторы кода.

Как итог можно смело прогнозировать, что через определенное время проект будет быстрее и дешевле переписать, чем развивать. Так что не используйте graphql

А бизнес логика как и положено лежит бизнес слое который собственно и инжектируется через DI в типы мутации и запросы, слава DDD при желании можно переписать все на REST тупо генерированием тех же REST ресурсов для каждого объекта логики.

Видимо, я использую какой-то другой GraphQL (((-:
Ну и не только я, всякие канторки поменшьше, типа Facebook ((((-:
Быстрее и дешевле делать на GraphQL. Так что используйте GraphQL!
У меня Apollo на фронте и на сервере.

В случае если использовать изоморфные приложения как у вас тогда все просто и понятно, но как эксперимент — попробуйте представить, просто представить что backend у вас не ваш любимый node.js а на java или например как в моем случае php.


Попробуйте описать 2 раза типы мутации и запросы ручками для клиента на том-же Apollo и следом за этим описать эти-же типы запросы и мутации на сервере используя например


  • php = laravel + folklore/graphql
  • java = Spring + graphql-java

я бы посмотрел на это =)


P.S
используйте GraphQL только если у вас изоморфное приложение на платформе node.js

Если Вы используете разные бэкенды — это не проблема GraphQL, это вопрос либо неправильной, либо странной архитектуры.
Клиенту фиолетово, что там на бэкенде, лишь бы он отдавал нужные ему данные в правильном формате. Если так сложно и больно делать бэкенд на php или чём-то там ещё, сервер на expresse поднимается несколькими строчками кода и забывается вся эта боль (-:
Посмотрел на graphql-java: не нашёл ничего страшного и сверхъестественного. У себя думаю для некоторых пользователей попробовать поднять GraphQL на Elixir-e.
Так что всё выше Вами описанное — это не проблемы GraphQL.

Разные бэкенды — это не неправильная или странная архитектура, а, ровно наоборот, выбор языка бэкенда лучше подходящего для той или иной задачи.


И дело не в том, что сложно сделать бэкенд на PHP, а в том, что схему графа придётся описывать дважды в случе сервера и клиента на разных языках. В общем случае — сколько языков используется с одной схемой — столько раз придётся описать.

Вот именно, если бэкенды для разного — это да, нормально использовать разное. В таком случае вообще не проблема поднять ещё крохотный бэкендик на expresse() и не иметь никаких проблем. (((-:
Может я не так понял, но выше предлагали писать одно и тоже на php и на java.
И опять же: это всё — не проблема GraphQL.
И дело не в том, что сложно сделать бэкенд на PHP, а в том, что схему графа придётся описывать дважды в случе сервера и клиента на разных языках.

А зачем на клиенте схему описывать?

С разными целями от поддержки инструментами разработки до валидации готового запроса на клиенте до отправки на сервер. Ну и есть клиенты, которые предоставляют высокоуровневый API для клиентских приложений на основании схемы.

Sign up to leave a comment.

Articles