[Разработка веб-сайтов, API] Как мы использовали GraphQL в разработке на примере интернет-каталога

Автор Сообщение
news_bot ®

Стаж: 6 лет 7 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
16-Сен-2020 11:31

В этой статье мы делимся опытом реального применения GraphQL при создании интернет-каталога. Рассказываем о том, какие достоинства и недостатки этого языка запросов мы нашли при использовании стека GraphQL + Apollo Client, Next JS (SSR) + TypeScript.
Дисклеймер: мы не описываем подробно «магическую суть» и историю возникновения инструмента (об этом уже рассказано немало). Надеемся, что тем, кто уже знаком с GraphQL, окажется полезен наш практический опыт.

Недавно мы приняли участие в создании интернет-каталога светотехнического оборудования. Для создания рекламных страниц администраторы сайта могли воспользоваться конструктором: выбрать нужные блоки (например, баннеры или списки), заполнить их данными, определить порядок отображения и другие настройки. При этом приложение рендерило компоненты, заранее сверстанные для каждого типа блоков.
В каждой категории интернет-каталога выводились карточки различных товарных групп, и при наведении на карточку отображался список свойств для вложенных товаров.
Нам нужно было выводить свойства товаров в древовидной структуре и обеспечить достаточно высокую скорость обработки запросов.
Мы выбрали следующий порядок работы с запросами:
  • Запросить все товарные группы для определенной категории (как правило, около 50 групп).
  • Запросить список товаров для каждой группы.
  • Запросить список свойств для каждого товара.

Поскольку мы разрабатывали приложение на базе GraphQL, мы были готовы к тому, что часть данных будет иметь достаточно сложную вложенную структуру. Хотя для backend-разработки ветвление этой структуры было логичным, на фронте нужно было писать некоторую «лишнюю» логику, чтобы обработать данные и вывести их в компонент, согласно дизайну.
Из-за особенностей конструктора GraphQL мы приняли решение осуществлять сбор свойств и уникальных значений не на бэке, а на фронте, а затем рендерить их в определенном порядке. Однако, обработка запроса происходила слишком медленно – до 20 секунд, что нас, конечно, не устраивало.
По этой причине мы стали делить каждый запрос на мелкие подзапросы и загружать данные порционно. В результате приложение заметно выиграло в скорости – запросы занимали не более 2 секунд. Хотя количество запросов стало больше, нагрузка на систему снизилась, исчезла необходимость подгружать неиспользуемые данные.
Далее расскажем подробнее непосредственно о работе с GraphQL.
Особенности работы с GraphQL
Согласно требованиям к продукту, нам следовало использовать язык запросов GraphQL, разработанный Facebook. По этой причине мы не стали пускаться в бесконечные споры, что лучше, GraphQL или REST – вместо этого мы решили использовать нужную технологию наиболее эффективным способом, учитывая все ее сильные стороны.
Мы учитывали, что GraphQL был разработан для упрощения разработки и поддержки API, прежде всего, за счет наличия единственной конечной точки.
GET  /news
GET  /posts
POST  /news
POST  /post

GraphQL имеет единственную конечную точку. Это означает, что для получения данных из двух разных ресурсов нам не нужно делать два отдельных запроса. GraphQL объединяет все запросы и мутации в одной конечной точке и делает её доступной для обращения, а также позволяет уйти от версионирования, свойственного REST API.
GraphQL обеспечивает возможность оптимизировать производительность и получать именно те данные, которые необходимы в данный момент, с помощью особого синтаксиса запросов: требуемые поля следует перечислить в запросе.
const FETCH_USER_DATA = gql`
query FetchUserData {
   user {
     firstName
     lastName
     date
   }
}
`;

GraphQL использует строго типизированные сущности и схему типов, что, в свою очередь, удобно в связке с TypeScript и генерацией типов на фронте.
Многие из перечисленных особенностей, безусловно, можно реализовать и на REST API, однако, GraphQL предоставляет их «из коробки».
Для взаимодействия клиента с GraphQL мы выбрали наиболее популярное решение с хорошей документацией – библиотеку Apollo Client, которая позволяет получать, кэшировать и модифицировать данные приложения. Apollo Client дает возможность использовать хуки для запросов и мутаций и инструменты для удобного отслеживания состояния загрузки/ошибки.
Также на фронте мы использовали фреймворк NextJS, выбранный с учетом следующих факторов: пре-рендеринг (NextJS предоставляет очень простой механизм реализации статической генерации и SSR “из коробки”), поддержка всех существующих css-in-js решений, динамический роутинг, поддержка статических файлов (например, изображений) в React-компонентах.
Наконец, когда технологии выбраны, перейдем к разработке. На первый взгляд, все смотрится неплохо: современные библиотеки, хорошая документация, много различных кейсов использования. Каждая из технологий по отдельности призвана способствовать комфортной и быстрой разработке. Создавая бойлерплейт, без которого было не обойтись, и верстая UI-компоненты, мы постепенно приблизились к этапу эффективного взаимодействия наших библиотек. Здесь началось все самое интересное.
Заглянув поглубже в механизмы NextJS, мы видим, что он использует две формы пререндера: статическую генерацию и SSR. Обе эти стратегии реализуются с помощью специальных функций предзагрузки данных:
`getInitialProps` (SSR) – при первой загрузке запускается на сервере, запрашивает данные и затем передает их в компонент в качестве props.
function Component ({data}) {
...
}
Component.getInitialProps = async (ctx) => {
const res = await fetch('https://...')
const json = await res.json()
return { data: json.data }
}

`getStaticProps` (Static Generation) – запускается на build-этапе. NextJS производит пре-рендер страницы с использованием props, вернувшихся из данной функции.
export async function getStaticProps(context) {
return {
   props: {}, // передает данные в компонент в качестве props
}
}

`getServerSideProps` (Server Side Rendering) – NextJS производит пре-рендер страницы при каждом запросе, используя данные, вернувшиеся из данной функции в качестве props.
export async function getServerSideProps(context) {
return {
   props: {}, // передает данные в компонент в качестве props
}
}

Таким образом, все перечисленные функции объявляются вне компонента, получают некоторые данные и передают в компонент. Отсюда вытекает одна из проблем взаимодействия с Apollo Client. Библиотека предоставляет такие механизмы запросов, как хук `useQuery` и компонент `Query`, при этом ни один из предложенных способов невозможно использовать вне компонента. С учетом этого в нашем проекте мы решили использовать библиотеку next-with-apollo и в итоге остались довольны результатом.
Еще одна известная проблема Apollo Client, с которой нам также довелось столкнуться – это возможность появления бесконечного цикла запросов из хука `useQuery`. Суть проблемы заключается в том, что Apollo Client продолжает бесконечно отправлять запросы до тех пор, пока успешно не получит данные. Это может привести к ситуации, когда компонент “повиснет” в бесконечном запросе, если сервер по какой-либо причине не может вернуть данные.
В нашем случае одной из причин служило изменение схемы на бэке. Apollo самостоятельно генерирует типы, поэтому при написании запросов на фронте мы должны были следовать сгенерированным типам, чтобы избежать ошибок. Например, у нас был рабочий запрос, без каких-либо проблем с типами. Однако, при изменении схемы на бэке одновременно менялись типы, из-за чего рабочий запрос мог перестать функционировать. Учитывая это, оптимально сразу внедрять логирование и четкую систему обработки ошибок, чтобы сэкономить нервы и время команды.
Достаточно полезным, на наш взгляд, оказалось то, что в graphql-запросах можно точно указать, какие данные следует получать. При отправке большого количества запросов это позволяет избежать обработки излишних данных. В свою очередь, это может существенно повлиять на производительность, когда растет количество данных.
Стоит отметить, что одним из преимуществ GraphQL считается удобство разработки и поддержки API, но это свойство более значимо для backend-разработки и, по нашим наблюдениям, не оказывает значительного влияния на фронт. Из-за генерации типов при каждом изменении схемы на бэке мы переписывали все затронутые запросы. Бэку также приходилось дорабатывать схему, если нам на фронте нужно было получить что-то, что еще не было реализовано. При этом генерация типов при использовании TypeScript позволяла отлавливать многие ошибки еще на стадии написания кода.
Подводя итоги
По нашим наблюдениям, GraphQL достаточно широко востребован в различных типах IT-решений и обеспечивает определенные преимущества для команды разработки. Суммируем основные особенности GraphQL, с которыми мы встретились при разработке проекта:
  • Генерация типов. Генерация типов graphql позволяет экономить время при написании запросов, идеально подходит для использования TypeScript и помогает отлавливать неприятные ошибки на раннем этапе разработки.
  • Быстрота обновления данных. С GraphQL в нашем случае можно было быстро обновить данные в фронтэнд-части приложения. У разработчиков была возможность вносить изменения на стороне клиента, не вмешиваясь в работу сервера (при наличии необходимых полей на бекэнде, конечно). При этом у graphql есть так называемая «песочница» для запросов, в которой можно протестировать запросы с разными параметрами
  • Обработка только нужных данных. В graphql-запросах можно точно указать, какие данные следует получать. При отправке большого количества запросов это позволяет избежать обработки излишних данных.
  • Масштабируемость. GraphQL делает удобнее работу с масштабируемыми системами, поскольку облегчает объединение данных из нескольких сервисов в один.

Спасибо за внимание! Надеемся, что этот пример был вам полезен.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_api, #_graphql, #_razrabotka (разработка), #_api, #_programmirovanie (программирование), #_javascript, #_react, #_blog_kompanii_simbirsoft (
Блог компании SimbirSoft
)
, #_razrabotka_vebsajtov (
Разработка веб-сайтов
)
, #_api
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 05-Окт 13:25
Часовой пояс: UTC + 5