Николай Ланец
9 янв. 2019 г., 6:51

Как формируются запросы в @prisma-cms

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

Дима, хороший вопрос :) Но не совсем корректный. Сейчас объясню.

За вопросы отвечает компонент apollo-cms. Это довольно старый компонент, который появился раньше, чем @prisma-cms, почти год назад. Вообще это довольно исторический вопрос :) Дело в том, что изначально я осваивал чистый GraphQL без каких-либо сторонних библиотек. Скажу так, это было увлекательно, но довольно АДово :) Вот немного статистика с сайта городских бань:
pm2 ls
┌───────────────┬────┬──────┬───────┬────────┬──
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem
├───────────────┼────┼──────┼───────┼────────┼───
│ bani │ 0 │ fork │ 6627 │ online │ 1 │ 34D │ 0% │ 608.2 MB

Не хило так, 600 метров оперативки на процесс? :) Бывали времена под полтора гига потреблял. Но работает стабильно. Это я просто недавно там гугл-карты обновлял по причине. А так месяцами работало без перебоя. Но все-таки текущие 46 метров на сайте Клуба приятней :)

Так вот, писать чистые запросы на GraphQL, а еще и обеспечивать на стороне браузера кеширование и прочее - это невероятная жесть. Мне сложно это объяснить в двух словах, но просто поверь на слово. Я как бани переведу на призму и смогу выложить исходники текущей версии сайта, запилю отдельные топик с примерами.

И вот, в какой-то момент я для себя открыл apollo-client. Официальный сайт часто не грузится (вероятно эхо войны с телегой), так что вот ссылка на тематическую статью с хабра. Много проблем для меня решила аполло, и перспективы всякие я увидел, но пришлось под себя подправлять, так что я и подумал, что это задел под CMS. Но в процессе выяснилось, что это все больше только под фронт, а надо же еще сервер, данные же надо хранить и обрабатывать. И хотя у аполло еще куча всяких плюшек есть, в том числе и сервер, но как-то все это слишком низкоуровнево и не юзабельно... И вот я для себя открываю prisma (на тот момент это еще называлось graphcool). Ну а дальше вы знаете...

Что примечательно, разработчики призмы много всяких аполловских вещей юзают, но они пошли дальше - они обеспечили интеграцию с базами данных. По сути они обеспечили полной двусторонний канал API-DataBase. Ну вот на базе этого я и развиваю свою CMS.

Возвращаемся к твоему вопросы :) И так, за выполнение запросов отвечает apollo-cms. Но сами запросы надо же еще откуда-то взять? Помнишь недавнюю статью про разворот modxclub.ru у себя локально? Там ключевой момент - это стянуть схему API сайта и сформировать на ее основе готовые фрагменты для запросов. Вот смотри один из запросов в @prisma-cms/resources:
const { queryFragments, } = this.context; const { ResourceNoNestingFragment, UserNoNestingFragment, } = queryFragments; const ResourceFragment = ` fragment Resource on Resource { ...ResourceNoNesting CreatedBy { ...UserNoNesting } } ${ResourceNoNestingFragment} ${UserNoNestingFragment} `;
Здесь из контекста берутся фрагменты типа ResourceNoNestingFragment и вставляются в тело запроса. Как видишь, здесь нет полей типа id. name, uri или типа того. Эти поля находятся в генерируемых фрагментах. Вот смотри, здесь, в API-схеме поля у ресурса перечислены. Когда я выполняю билд, эти поля всех объектов набиваются во фрагменты и упаковываются в проект. Далее, в процессе работы эти фрагменты передаются в такие запросы и отправляются на сервер. Пример такого запроса здесь:
query topic($where: ResourceWhereUniqueInput = { uri: "/topics/reklama-na-sajte-kluba-1211.html" }, $getCommentsText: Boolean = false) { object: resource(where: $where) { ...topicsFullFragment __typename } } fragment topicsFullFragment on Resource { ...topicFragment content __typename } fragment topicFragment on Resource { ...ResourceNoNesting CreatedBy { ...UserNoNesting __typename } Comments(orderBy: id_ASC) { id uri createdAt updatedAt content @include(if: $getCommentsText) CreatedBy { ...UserNoNesting __typename } __typename } Blog { id name longtitle uri __typename } Tags { Tag { id name __typename } __typename } __typename } fragment ResourceNoNesting on Resource { id createdAt updatedAt name longtitle content contentText published deleted hidemenu searchable uri isfolder rating positiveVotesCount negativeVotesCount neutralVotesCount oldID commentOldID class_key template mockUpdate type __typename } fragment UserNoNesting on User { id createdAt updatedAt username email phone showEmail showPhone password fullname image address sudo active activated deleted hasEmail hasPhone __typename }
Большой, не правда ли? :) И это еще не самый большой запрос. Согласись, такие запросы писать - хлопотно. Так вот, с таким механизмом не надо из писать полностью. Достаточно только основную часть без фрагментов написать и все. В нашем случае это что-то типа
query topic($where: ResourceWhereUniqueInput = { uri: "/topics/reklama-na-sajte-kluba-1211.html" }, $getCommentsText: Boolean = false) { object: resource(where: $where) { ...topicsFullFragment __typename } } fragment topicsFullFragment on Resource { ...topicFragment } fragment topicFragment on Resource { ...ResourceNoNesting CreatedBy { ...UserNoNesting } } fragment ResourceNoNesting on Resource { ... } fragment UserNoNesting on User { ... }
Вот такие заготовки и попадают в компоненты типа @prisma-cms/resource, @prisma-cms/society и т.п. Кстати, в @prisma-cms/society гораздо больше запросов-заготовок (посмотри сейчас, наверняка будет уже более понятно). Помимо заготовок под запросы есть еще заготовки-слушатели для веб-сокетов (это то, что отвечает за фоновое обновление информации на странице). Разница между этими двумя типами компонентов в том, что ContextProvider только передает заготовки запросов, но не выполняет их, а вот подключаемый SubscriptionProvider сразу добавляет соединения для веб-сокета. То есть просто подключаешь такой и все, в браузер получаешь актуальные изменения по подписанным типам.

За передачу всего этого внутрь компонентов обеспечивает совсем маленький, но волшебный компонент @prisma-cms/context. Сейчас его код вот такой:
import { createContext, } from 'react'; export default createContext({});
Да, это все :) Но это сильнейшее колдунство! Без него бы все это не работало, как работает сейчас. Когда вы чуть прокачаетесь в освоении текущих технологий, я напишу статью про него отдельную и подробную. Там очень круто все!

А на счет заготовки... Я писал в позапрошлом топике для чего какие заготовки сейчас существуют. Спойлер: все фронтовое есть суть производное от @prisma-cms/component-boilerplate.

Ничего-себе комментик) Спасибо! Изучаю)

Добавить комментарий