Comments 72
Потому что он представляет собой структуру данных в виде графа, где узлы графа представляют собой объекты, а рёбра - связи между этими объектами.
GraphQL не представляет собой структуру в виде графа. Он представляет собой дерево, со всеми нюансами в виде избыточности взаимной вложенности сущностей.
GraphQL позволяет клиентам запрашивать только ту информацию, что им нужна. Это уменьшает время ответа от сервера и количество данных, которые нужно передавать по сети.
Позволяет запрашивать, вот только может сильно увеличить время ответа сервера. Это как с прямыми SQL запросами. Много сложности тут перекладывается на плечи разработчика сервера. То есть никак не отменяет необходимости мероприятий по оптимизации. А еще открывает кучу дыр, которые потом требуется затыкать.
Я даже как-то делал свой GraphQL для одной энтерпрайз-экосистемы, где использовать именно GraphQL было нельзя по многим причинам. Мое решение было основано на JSON и умело почти все тоже самое, что умеет слой обработки GraphQL, включая типизацию. И вот что я вам скажу: если вы делаете что-то массовое, GraphQL сильно поднимает требования к уровню разработчиков.
В статье сильно не хватает секции о том, где GraphQL применим, а где лучше держаться от него подальше.
По моему опыту, лучшее применение для GraphQL - это внутреннее непубличное API. Гибкость GraphQL позволяет фронтендерам меньше дергать бэкендеров когда надо немного поменять интерфейс. На этапе сборки и деплоя написанные фронтендерами query файлы добавляются в «белый список», и в продакшене сервер работает уже практически как обычный REST, который на конкретные вызовы из ограниченного массива разрешенных присылает конкретные ответы с конкретной схемой. И волки сыты, и овцы целы, и разработка ускоряется, и никто не положит сервер неадекватно вложенным запросом.
А вот для публичных API GraphQL лучше реализовывать как промежуточный слой, который под капотом вызывает обычный REST. Если API небольшое, то смысла в GraphQL особо нет.
в зависимости от того как ты работаешь с графкулем, можно построить как один большой запрос, так и несколько атомарных запросов попроще, все зависит от твоих потребностей. говорить, что ты написал свое легаси и оно лучше технологии гкл, даже если это и действительно так, это минимум оффтоп.
Хотел бы ещё спросить, сталкивался ли кто-то с проверкой безопасности проектов с использованием GraphQL? Есть ли какие-то полноценные инструкции, рекомендации или неочевидные моменты, на которые стоит обратить внимание?
Нужно контролировать сложность запроса, вводим весовые коэффициенты. Нужно работать с бинарными данными, делаем ещё и REST. Нужно разные права, делаем вложенные ролевые резольверы или несколько GraphQL апишек. Как заметили в комментарии выше, требования к уровню разработчика повышаются.
вот тут я соглашусь с коллегой, предложение гкл отдавать файлы через base64 не проходит никакой критики, по любым файлам в гкл должна быть исключительно мета информация, физически данные забираем с рест стореджа.
по практике могу сказать, что я не поставил бы во главе анализа весовые коэффициенты, мой путь - разрез графа, где я сознательно из запроса А не пропускаю ниже определенного слоя вложености.
Условно
queryA getObj { obj { structure, meta } <--- вы
query B getContent(structureId: ...) { content ....}
Разрыв графа не позволяет вытащить половину базы данных, дает возможности фронтам на свободу действий, но в тоже время защищает бек от избыточной выборки с бд. Пример синтетический и может быть не понят вне реального кейса, но возможно кому-то будет это полезным.
REST - хорошая штука, но у него есть некоторые проблемы с которыми и столкнулся Facebook:
Во-первых, это избыточность или недостаток данных в ответе.
Также в REST API каждый эндпойнт обычно соответствует определенному ресурсу
для получения связанных данных нужно делать дополнительные запросы к серверу
json api сделали возможность лимитирования количества запрашиваемых вложенных объектов?
В HARP сделали.
Я правильно понимаю, что вы автор этого проекта?
Конечно.
Вы так смело свой инди проект поставили в один ряд с известными проектами. При этом вас там двое разработчиков, и возраст проекта, если считать по коммитам, год и два месяца с хвостиком.
Поймите меня правильно, я ценю инди разработку, не будь инди разработки у нас не было бы того же линукса. И вы со своим напарником ребята крутые, и проект, вероятно, неплохой, но вам скорее статью отдельную статью о проекте написать с примерами кода и т.п. через эту же статью контрибьюторов в команду зазвать, документацию дополнить, поддержку языков сделать, чтобы не только typescript был, а ещё хотя бы условный python.
Так запилите на python, если вам надо, какие проблемы? С этим справится любой джун на собеседовании.
У меня нет проблем, под мои задачи graphql подходит. Для задач, где он не подходит я скорее буду рассматривать зрелые проекты, где есть команда разработчиков, вокруг которых есть сообщество. Ваш проект этими вещами пока похвастаться не может.
А, так у вас проблема в другой плоскости, - думаете лишь чужой головой, от того и не видите своих проблем. Предложенное выше надругательство на gql возвращающее проблему 1+n для решения проблемы оценки сложности, довольно красноречиво.
Что именно из того что я говорил возвращат проблему n+1 запроса?
Если вы про лимитирование количества связанных объектов в вопросе про json:api, так то что если ребята из нетфликса (они вроде в этот протокол вкладывались) на этот счёт не заморочились, ещё не говорит о том, что это невозможно. Посмотрите как сделано в hasura или postgraphile, и я ещё что-то подобное как то рассматривал.
Я вам тоже не про код ваш говорю. Я вам говорю, что вы всесто развития сообщества пишете "вон в graphql плохо тем то", я не спорю хотя есть relay построенный на graphql, который вашу таблицу со сравнением дополняет.
Но ключевое другое: вот мы в команде пользуемся либой, где почти пятьсот контрибьюторов, и даже захоти я в работе использовать ваш, назовём это фреймворком, директор меня в этом не поддержит. Если ваш проект соберёт команду и сообщество, я даже выделю пару дней времени чтобы его попробовать, и не исключено, что буду рекомендовать его как альтернативу graphql, можете напоминание сделать. Но пока это инди проект с непонятными перспективами, и основной мой именно об этом.
Зачем мне ваше вкусное яйцо, когда я уже привык есть не вкусных куриц?
Вам 20 раз уже объяснили, и не только в этом обсуждении, что характеристику "вкусное" люди определяют по другому критерию, а не по вашему. А вы почему-то ожидаете, что всем должны быть важны те же критерии, что и вам.
Это вопрос не вкуса, а низкой технической культуры. Сами тех анализ провести не в состоянии, и даже не пытаются, полагая, что некое мифическое (внешнее!) сообщество волшебным образом выдвинет лучшее решение, а не посредственное. В результате мы получаем рождённых в муках чудовищ, обрекая себя и преемников на долгие годы борьбы с ними, вместо того, чтобы решить эти проблемы в корне за пару дней времени.
Вы для всех остальных такое же мифическое внешнее сообщество, которое может выдвинуть посредственное решение. Непонятно, почему другие люди должны надеяться на вас, а не на другое сообщество.
Сами тех анализ провести не в состоянии
Тех анализ вашего решения люди уже провели и объяснили почему оно им не нравится. Анализ решений это и есть определение критериев, по которым их можно сравнивать. По вашим критериям ваше решение лучше, а по критериям, которые используют другие люди, лучше другие решения.
вместо того, чтобы решить эти проблемы в корне за пару дней времени
Ну вот в том и дело, что вы не понимаете, что в большинстве случаев у людей вообще нет необходимости решать эти проблемы. Не нужна никому дедупликация, если надо просто вывести заказы в цикле. А там где нужна это точно так же решается за пару дней, если не за пару часов.
А чем python для веб-проектов лучше PHP, или Ruby, или Java, или C#, или Go, или Rust? На JS/TS можно написать и фронт, и бэк. Более того, можно написать код, который будет работать и на фронте, и на бэке. Так зачем тянуть в проект другие языки, когда можно обойтись одним? Старина Оккам бы не одобрил такое.
Заметьте, я не про "python" не, а про "условный python" говорю, разница существенная. Если авторы хотят, чтобы проект развивался, значит поддержка других ЯП будет плюс, привлечёт больше энтузиастов и т.д. и т.п. Пусть даже поддержка в формате "вот Вася Пупкин запилил поддержку на байткоде для ZX80" она проекту только в плюс пойдёт.
Так и я о том же. Для разработки веб-приложений, имеющих UI в браузере, достаточно одного языка и для бэка, и для фронта. Зачем делать платформу для двух-трёх-... языков? Это не даст популярности среди "не-веб" разрабов, но заберёт имеющиеся ресурсы. А веб-разрабы уже умеют в JS/TS.
Пусть даже поддержка в формате "вот Вася Пупкин запилил поддержку на байткоде для ZX80" она проекту только в плюс пойдёт.
Я бы не хотел в своём проекте отдуваться за кривую реализацию Васи. Более того, я рекомендую всегда оценивать границы применимости инструмента перед его применением. А так-то и молотком можно шуруп "закрутить".
Вы так говорите, словно не существует бэкэндов написанных на ruby, python, php, rust, golang, perl я даже не знаю чём ещё.
2. Речь не про "вы будете поддерживать", а то что вокруг проекта будут люди, которые будут его развивать и поддерживать. Сейчас в репозитории обсуждаемого проекта 2 контрибьютора, один попадёт в больницу, другой уйдёт в буддистский монастырь и проект развивать будет некому.
Речь не о том, "что проект молодой, фу-фу-фу", все зрелые проекты били молодыми. Речь о том, что молодой проект на мой взгляд поспешно ставится в один ряд со зрелыми проектами, пусть даже зрелые проекты со своими проблемами.Фронтэнд это не всегда то, с чем работает конечный пользователь в браузере. Я под задачу graphql запросы на баше делал с применением утилит jq и curl. А могу захотеть на условном Delphi фронтэнд написать, как бы вы не утверждали, что это не нужно.
Для разработки веб-приложений, имеющих UI в браузере, достаточно одного языка
Я об этом и только об этом. Если UI на Delphi, то и бэк разумно делать на нём же.
Если речь про малые команды, где бэкэндеры и фронтэндеры это одни и те же люди, я с вами соглашусь. Для чуть более крупных команд и проектов, спорно.
В контексте обсуждаемого проекта, я соглашусь в формулировке, что искать надо скорее людей которые TS/JS умеют, когда проект достигнет "критической массы", питонисты, рубисты и прочие товарищи к нему сами подтянутся.
А, всё, понял. Вы про HARP и только про HARP говорили, а я про $mol в целом. Да, конечно, если развивать именно HARP, то тут нужно максимальное покрытие по различным ЯП. Согласен.
В целом да. Скорее даже не столько про сам HARP, а HARP в контексте "Есть Я, есть Пифагор, есть Ньютон и есть Эйнтштейн".
За денормализацию в ответах GQL можно смело отчислять такого горе-проектировщика графовых протоколов из школы. В FB что ни велосипед (привет, React и Redux), то с квадратными колёсами.
Слушайте у gql есть проблемы они известны, вы тут не первооткрыватель. Но ваш протокол ему не конкурент и не будет, причем походу в первую очередь из за вашей позиции в обсуждениях.
На GraphQL пример с вашей страницы тоже решается тривиально.
{
person(id: 'jin', friendsTillLevel: 2) {
id
name
friends { id }
allFriendsTillLevel {
id
name
friends { id }
}
}
}
Возвращается такой же ответ, как у вас, только не индексированный по id.
{
"person": {
"id": "jin",
"name": "Jin",
"friends": [
{"id": "alice"},
{"id": "bob"},
],
"allFriendsTillLevel": [
{
"id": "alice",
"name": "Alice",
"friends": [
{"id": "bob"},
{"id": "jin"},
]
}
{
"id": "bob",
"name": "Bob",
"friends": [
{"id": "alice"},
{"id": "jin"},
]
}
]
}
}
Предупреждая возможный вопрос — нет, я не хочу индексировать массив по id на сервере для каждого запроса для каждого из миллиона пользователей только из-за того, что вам лень написать такую функцию на клиенте.
Ответ из вашего примера не отражает уровень вложенности друзей, который запросили (запросили 3, а пришел плоский список), и непонятно как обрабатывать ответ если на верхнем уровне запросили список person, а не одну по id. Придет 10 объектов под ключом person, какие из них это результат запроса с фильтром, а какие их друзья? Также непонятно, как запросить разный набор полей для person верхнего уровня и для друзей.
Вы тривиально вставили костыль для одного частного случая. Очень быстро такими темпами число таких костылей, требующих отдельного внимания со стороны бэкендера, технического писателя, и всех пользователей вашего апи, будет многократно больше, чем число собственно бизнес-сущностей.
Корневые узлы перечислены в _query//reply
, а дальше по ссылкам. Выбираемые на каждом уровне поля явно перечислены в запросе.
будет многократно больше, чем число собственно бизнес-сущностей.
Нет. Я работал с GraphQL, такие приемы мне вообще ни разу не понадобились. Равно как техническим писателям и пользователям API. Случаи где дедубликация на что-то повлияет встречаются довольно редко.
Корневые узлы перечислены в _query//reply, а дальше по ссылкам.
А, вот зачем оно там) Ок. Хотя тащить в функцию обработки ответа весь запрос чтобы там перевести его в строку, обратиться по этому индексу и достать фактический ответ сомнительное удобство. При том что при отправке несколько запросов идут одной строкой, а в ответе они в отдельных полях. В GraphQL я точно знаю, что там всегда будет "person" независимо от фильтра.
Обработать результат через for (let friend of person.friends)
тоже все-таки удобнее, чем лазить по ссылкам.
Да-да, довольно редко. Вы вот прямо сейчас смотрите на дерево комментариев, для каждого из которых, если без костылей, в GQL продублируются данные дискутирующих пользователей.
Этим всем занимается harp-адаптер, который универсально работает со всеми сущностями. Вы ему запрос, а он вам срез графа. Безо всяких бестолковых "функций обработки ответа" на каждый новый запрос.
Этим всем занимается harp-адаптер
Да-да, который я должен поставить и настроить на сервере, чтобы он успевал это делать для всех одновременно работающих пользователей. Да еще так, чтобы он не устроил мне N+1 с базой, строя этот самый срез графа. Поэтому я предпочту перенести дедубликацию на клиента, если это возможно.
Безо всяких бестолковых "функций обработки ответа" на каждый новый запрос.
Как же вы собрались обрабатывать ответ от сервера без функций обработки ответа? У вас они будут в любом случае. Одна рендерит меню, другая сайдбар, третья комменты. Вот и вызовете там friends = Utils.indexBy(person.friends, 'id')
если нужно, и не вызовете если не нужно. Зачем мне это на сервере.
Вы вот прямо сейчас смотрите на дерево комментариев
Факт существования комментариев на Хабре, представьте себе, никак не меняет характеристики приложений, в которых я использую GraphQL. В моих приложениях дедубликация была не нужна, и даже там, где дублирование было, никаких проблем оно не создавало.
в GQL продублируются данные дискутирующих пользователей
Допустим. И что? Какая именно проблема от этого появляется?
Вот запрос комментов этой статьи.
https://habr.com/kek/v2/articles/765064/comments/?fl=ru&hl=ru
Данные занимают 93 кб, только они передаются в gzip, и поэтому передаётся 20 кб.
Если дедублицировать информацию об авторах, будет 84 кб, а в gzip внезапно будет те же 20 кб.
Я запаковал локально для проверки, разница получилась 200 байт. Одна картинка в этой статье весит в 1000 раз больше.
То есть gzip и так прекрасно справляется с дублирующимися данными. Зачем тут нужен еще и ваш протокол?
Кстати, вас не смущает, что эти данные дублируются в самом интерфейсе? То есть вы предлагаете зачем-то дедублицировать, а потом дублицировать обратно при рендеринге.
Адаптер используется на клиенте, и абстрагирует прикладную логику от деталей протокола общения с сервером.
Зачем вы дублицируете данные из базы, которая хранит их в нормальной форме (ведь в нормальной же, Эникен?), чтобы потом клиент их дедублицировал - одному Ктулху известно. В своей статье я рассказывал историю из жизни про это - там бэкендер тоже не видел проблем, пока не посыпались жалобы от пользователей, что у них браузер крешится при открытии.
Гляньте лучше эту выдачу: https://habr.com/kek/v2/articles/423889/comments/ - 2.5к комментариев, 3 метра данных, требующих сжатия, разжатия, парсинга, валидации и дедупликации.
При использовании виртуалного рендеринга, данные на странице не дублируются. Лишь немного в пределах видимой области. И то, дублируются лишь представления данных, а не сами данные.
Адаптер используется на клиенте, и абстрагирует прикладную логику от деталей протокола общения с сервером.
Это какой-то нонсенс. У меня на сервере есть массив с порядковыми индексами. Вы утверждаете, что я могу его просто отправить как есть, а вы на клиенте с помощью адаптера превратите его в массив, проиндексированный по первичному ключу? Тогда зачем тут вообще какой-то протокол?
Также непонятно, как к этому относится фраза "Вы ему запрос, а он вам срез графа". Адаптер с клиента лезет в базу за данными?
Или все-таки я на сервере должен иметь какой-то код, который будет делать дедубликацию и индексацию нужных массивов по первичному ключу в соответствии с запросом и формировать ответ в формате HARP? Я говорю про этот код. Это он строит срез графа, а не клиент.
Зачем вы дублицируете данные из базы, которая хранит их в нормальной форме
1. Потому что это вы не хотите работать с нормальной формой и получать в ответе только userId.
2. База хранит в данные нормальной форме, в приложении они превращаются в объекты в оперативной памяти с некоторыми адресами. Один объект имеет ссылку на другой в виде адреса, а никак не первичного ключа. Поэтому как данные хранятся в базе к рендерингу ответа API не имеет никакого отношения.
3. Дальше у меня в приложении есть orders[1].user
и orders[2].user
, которые мне надо перевести в строковую форму. Они потенциально могут ссылаться на один и то же объект. Я не могу отправить на клиент адреса объектов user, потому что вы с клиента доступа к этим адресам не имеете. Это и есть ответ на вопрос зачем — затем что у вас на клиенте нет доступа к моим нормализованным данным в оперативной памяти. Затем чтобы доступ к этим данным у вас появился.
Мне удобнее просто взять список нужных полей из orders[i].user
. Фронтендеру тоже обычно удобнее просто вывести результат через orders[i].user
.
А чтобы превратить нормализацию по адресам в нормализацию внутри строки ответа, нужна дополнительная обработка. Она включает такие шаги как:
— проверить что объект это сущность с первичным ключом, а не какой-нибудь DateTime
— получить значения полей первичного ключа
— обработать составной первичный ключ
— положить объект в общий массив по этому ключу
Она занимает не так уж много ресурсов, но она все равно не бесплатная, и без причины для каждого массива в каждом запросе я это делать не хочу. Сервер один, а клиентов много.
3 метра данных, требующих сжатия, разжатия, парсинга, валидации и дедупликации.
Проверил, разница незапакованных данных 200 кБ, всего 7%. Запакованных 28 кБ, 4%.
Одна картинка все еще весит больше. Чем больше в статье картинок, тем меньше будет процентная доля экономии на передаче данных.
Нет, для рендеринга комментов дедупликация не нужна.
"парсинга"
Ага, то есть вы утверждаете, что парсинг 2.8 Мб вашего протокола кодом на JavaScript будет выполняться быстрее парсинга 3.0 Мб JSON данных кодом на C++? Конечно же нет.
Клиент работает с сервером через графовую модель. Она может ложиться 1-к-1 на графовую БД, либо через какой-нибуд ORM мапиться на таблички. Никакой дубликации тут не требуется нигде.
На клиенте json-деревья тоже нафиг не сдались. Тут тоже строится графовая модель, где можно спокойно ходить по графу в любую сторону. Всё по первичным ключам, никаких прямых ссылок.
HARP payload может быть в том числе и в формате JSON. Вы бы лучше статью прочитали внимательно и не городили чушь.
Никакой дубликации тут не требуется нигде.
Ну так я вроде понятно написал, что у меня на сервере никакой дубликации нигде нет.
На клиенте json-деревья тоже нафиг не сдались.
Ну так я вроде и не говорил, что сдались. Преобразуйте ответ сервера как вам надо и работайте с этой формой. Непонятно с чем вы спорите.
HARP payload может быть в том числе и в формате JSON.
Ну так вы-то в статье рекомендуете использовать ваш формат Tree. JSON я могу и так вернуть, с ним и никакой адаптер HARP на клиенте не нужен. Сделал JSON.parse() и получил готовый ассоциативный массив, по которому можно ходить по первичным ключам.
Всё по первичным ключам, никаких прямых ссылок.
Ну так такая форма данных не магически появляется, сервер ее должен построить и затратить на это ресурсы. Потому что сервер ходит по графу по ссылкам, а не по первичным ключам. Но вы похоже не понимаете, о чем я говорю. Или делаете вид.
А поддерживается ли серверными библиотеками такой юз-кейс: жёсткой схемы на сервере нет, но запросы обрабатываются единообразно? Например, можно ли просто смапить GraphQL на SQL, добавив свою авторизацию и дополнительную логику? (Для работы с произвольной базой).
Просто смапить не выйдет из-за разных типов данных. Наверное, вы смотрите в сторону Object Database, где модель изначально задаётся типами GraphQL. Наподобие Mongo для JSON.
Просто смапить не выйдет из-за разных типов данных.
А если не просто, а через ORM?
Я вот смотрю на примеры возвращаемых результатов через GraphQL и у меня перед глазами прямо встают типы DTO для Entity Framework (это ORM для C#, если кто вдруг не в курсе). Остается только прикрутить библиотеку преобразования запроса c языка GraphQL в LINQ — и можно работать с реляционной БД, не сильно при этом задумываясь.
Для этого же odata есть, она генерит expression, который куда угодно потом можно применять.
Я работал с GraphQL довольно долго, пробовал разное, и вот этот подход я очень не рекомендую.
По сути GraphQL сдвигает задачу формирования структуры из разрозненных данных с клиента на сервер - вместо десяти запросов на сервер при рендере страницы у вас один GraphQL запрос, сервер как бы сам укомплектовывает эту корзину и присылает в готовом виде. Возникает соблазн сдвинуть эту задачу еще на уровень ниже, то есть на уровень БД. Проблема в том что тогда бизнес логике на сервере придется работать не с плоскими структурами, а с очень динамичными и вложенными данными, вы будете постоянно путаться и ваш код на любом языке превратится в динамически типизированный Python. Возможно не очень хорошо передал мысль, но лучше не получается :(
Второй момент - нагрузка на БД становится непредсказуемой. Иногда может быть лучше просто сделать 10 SQL запросов, чем один запрос с 10 join’ами.
Возникает соблазн сдвинуть эту задачу еще на уровень ниже, то есть на уровень БД.
Вроде как, между уровнем БД и уровнем бизнес-логики часто бывает уровень абстрактного хранилища объектов: хранилище отображает объекты, с которыми работает бизнес-логика, на операции уровня БД. И ORM как раз этим и занимается.
Так что сдвигать задачу на уровень БД — ее схемы и операций с ней — в общем случае совсем не обязательно. При низких требованиях к производительности можно обойтись ORM и тем самым сэкономить на персонале (а бизнес экономить любит, да), которому не нужно уметь работать на уровне БД: знать SQL и, тем более, язык хранимых процедур конкретной БД, и, тем более, читать планы выполнения запросов, думать о необходимости индексов, избегать излишних блокировок и заниматься прочими обязанностями DBA.
Бизнес-логика же при наличии такого абстрактного хранилища в виде ORM будет работать с объектами вполне статических типов (не скажу за все ORM, но конкретно EF это более чем позволяет: это его основной режим работы).
Все это замечательно здравствует, пока не возникают нагрузки, для которых требуется оптимизация, хоть какая-то. Но это уже другая история.
А если вспомнить исходный комментарий ветки от ImagineTables то, безотносительно к GraphQL, схема данных желательна (ибо дисциплинирует), но, накрайняк, без нее можно обойтись (например, так обстоит дело в мире XML). Проверять же запрос на правильность формулирования можно и при его приеме на севере. Правда тогда есть риск возникновения разных представлений о схеме данных у разных людей — и вот тут-то схема и проверка по ней станет настоятельно необходимой.
А если вспомнить исходный комментарий ветки от ImagineTables то, безотносительно к GraphQL, схема данных желательна (ибо дисциплинирует)
Ну, я-то речь вёл не о соблазнах, в которые ввергают нас демоны архитектуры, а о, так сказать, свободе (она же — осознанная необходимость). Свободе от схемы, потому, что её нет by design. Понятно, что какой-то зайчаток схемы всё-таки есть, но чисто для бутстраппинга (чтобы передавать произвольный список колонок сначала нужен какой-то жёстко заданный список колонок).
Для чего это нужно на практике? Например, для приложений типа lists.live.com. (Которое недавно релизнула Майкрософт переодев Шарика). Или для low code/no code платформ. Для всяких CRM, там такое любят. Да даже для качественно сделанного офисного органайзера это пригодится (нельзя же назвать качественным органайзер, где выбираешь из тысячи заранее заданных колонок, а свою сделать не можешь).
Теперь, надеюсь, понятно, что «смаппить на SQL» — это просто пример, когда отсутствует схема? (Вместо неё есть только какие-то соглашения, например, о кодировании семантики типов в названиях колонок). Реально это может быть и SQL, и файлы, и даже git. (Нуачобынет, если хочется дать юзерам тип колонки «версионированный файл»).
Общаясь с одним разработчиком, я услышал, что такие вещи нельзя делать на REST API, а нужен обязательно query language. Минимизировать запросы и так далее. А я ответил, что получается какой-то нездоровый дополнительный слой, ибо на сервере надо писать парсер. (Про себя я подумал, что знаю, где обычно заканчивается такая дорога: написанием своего SQL Blackjack Edition, нестандартного, кривого, глухого и с багами). А тут вот я вижу, что нет, люди себя сумели ограничить. И выглядит полезно. Если я правильно понял, спроектировано так, что от рутины ты избавлен и можно просто писать резолверы… или нет? Для кейса с отсутствующей схемой?
Конечно, надо бы просто скачать https://github.com/graphql/libgraphqlparser и покрутить его, но может кто-то внесёт ясность сразу на концептуальном уровне :)
Гляньте на утилиты вроде hasura и postgraphile.
Я пока посмотрел postgraphile, и так понял, что там из схемы БД генерируется схема GraphQL (и сервер заодно). А я говорил про случай, когда жёсткой схемы вообще нет (она динамическая).
Базовый ответ, нет, в graphql используется строго типизированная схема. Можно конечно прикрутить, но так можно предложить и на ассемблере веб приложения писать.
А что значит "динамическая схема"? В базе условные документы хранятся, и в них разный набор атрибутов? Думаю такое тоже можно сделать, можно посмотреть на то, что сделали разработчики MongoDB, т.к. монго, насколько я помню, предоставляет graphql api для базы.
А что значит "динамическая схема"?
Например, вот: https://lists.live.com/
Это, конечно, можно сделать по фиксированной схеме. Если я правильно понимаю, на базе чего они сделали это приложение, то у них в БД как раз и используется фиксированная схема (там хранится что-то типа Int, Int2 … Int16, Varchar1, Varchar2 … Varchar16, Float1, Float2 … Float16). Но в объектной модели у них доступ к колонкам разрешён по именам, заданным юзером (что-то типа Tables["MyTable"].GetElement(0).SetFieldValue["MyIntField", 42);). Мне интересно, можно ли в таких случаях сделать транспорт между клиентом и сервером на GraphQL, а на стороне сервера малой кровью преобразовывать запросы в вызовы объектной модели (имплементируя резолверы)
У вас получается в каждый момент времени есит фиксированная схема, просто она достаточно легко меняется. что, в общем то ок, вряд ли вы на всём жизненном цикле своего проекта будете ежедневно новые столбцы добавлять.
Под вашу задачу можно сделать получение схемы базы данных, скажем, при инициализации, плюс набор "умных" резолверов, которые будут по схеме базы данных создавать соответствующие типы, поля, возможно и связи, хотя со связями сразу слой сложности появится, но технически не невозможно, и, пожалуй, оправданно.
Естественно, есть рекомендация "не удалять поля из api", если они вдруг стали не нужны, чтобы старые клиенты внезапно не ломались, и в обсуждаемой реализации она к ней прибавится "не удалять столбцы из таблиц". Но это именно что рекомендация для публичных api, а не закон.
вряд ли вы на всём жизненном цикле своего проекта будете ежедневно новые столбцы добавлять
Не «мы», а «пользователи» :) И да, именно что ежедневно. В реальности они меняют схему столь же часто, как создают документы (вместо документа они создают новую таблицу). В этом и смысл таких систем.
Под вашу задачу можно сделать получение схемы базы данных, скажем, при инициализации, плюс набор "умных" резолверов, которые будут по схеме базы данных создавать соответствующие типы, поля
То есть, есть смысл использовать для таких задач GraphQL? И делать всё на резолверах, а не через REST API?
Я так вам сразу не скажу. Будь у меня по работе подобная задача, я бы на пару недель в думалку закинул для размышления на фоне прочих задач, посмотрел бы на варианты, обсудили бы с коллегами, поругались бы пару раз, для порядка.
Технически сделать можно, даже если каждый день добавляются новые столбцы, по идее технически можно реализовать, хоть с перезагрузкой схемы по 20 раз в минуту. В конце концов Graphql это не фреймворк, а протокол. У меня основновной вопрос как правильно разруливать удаление и переименование столбцов в таблицах.
GraphQL это язык для клиентов API, он подразумевает указание в запросе конкретных названий полей. Если вы знаете, какие поля есть у сущности, и можете указать их в коде клиента, значит у вас и так есть схема. Если не знаете, тогда непонятно, как вы хотите составлять запрос.
Конкретно для этой задачи можно сделать более высокий уровень абстракции — будет сущность List { fields: Field[] }
и Field { name: string, type: string }
.
Когда-то имел какое-то отношение вот к этому проекту - https://pypi.org/project/damvitool/ Базовая идея была такая: на серверной стороне при подключении в БД (MySQL или PostgreSQL) делался анализ имеющихся таблиц и отношений между ними (foreign keys), после чего модель данных в виде JSON отправлялась на фронт и использовалась для построения запросов к базе при помощи графического интерефейса. В общем, можно было через конструктор визуализировать структуру данных произвольной базы и давать пользователю возможность составлять запросы произвольной вложенности без знания SQL.
Да, это как раз пример того, когда схема заранее не задана.
(Жаль, что на странице толком не показан UI. Ведь если цель — конечные пользователи «без знания SQL», это же становится самое важное).
Что-то типа такого предполагалось. 4 - это браузер по сущностям (таблицам), в узлах (фолдерах) - связи с другими сущностями (foreign keys). 5 - это условия связывания сущностей (таблиц) по ключам, 6 - результирующая выборка (данные).
В основе лежала идея дать возможность операторам делать произвольные выборки данных и сохранять запросы в своих favorites в веб-приложении. Принципиальную модель сделали на php/angular, затем сделали красиво на python/smartclient (вот этот скриншот), а потом забросили.
Есть объектные базы, самая известная Cache
Ну и куча обьектных nosql через json
один запрос к эндпойнту пользователей для получения списка пользователей и запрос к энпойнту постов, у которого мы запросим посты для всех найденных пользователей
А что мешает сделать эндпоинт для получения списка нужных постов с нужным набором данных, где логика будет прописана не на клиенте, а на сервере?
А что мешает сделать эндпоинт для получения списка нужных постов с нужным набором данных, где логика будет прописана не на клиенте, а на сервере?
Если у вас маленькая команда то ничего не мешает. А вот в крупных компаниях frontend и mobile делает отдельная команда, и получается неудобно на каждую фичу просить backend команду вставить новый endpoint.
У них там свой график и план релизов, и из-за бюрократии становится сложно согласовать изменения/просить добавлять новые фичи.
В статье о этом не написано, но самое главное достоинство GraphQL именно в этом. Это ещё один стандартизированный QueryLanguage который позволяет получать все что нужно фронту не отвлекая бэк.
return users.filter(user=> user.id=== args.id)[0]
серьезно?
Что такое GraphQL