Шаблон BFF оптимизирует серверные части для конкретных клиентских интерфейсов. Использование GraphQL устраняет некоторые из его недостатков, но можем ли мы добиться большего? Давайте узнаем с WunderGraph.

Шаблон Backend-for-frontends (BFF), впервые описанный Сэмом Ньюманом и впервые использованный в SoundCloud, является эффективным решением, когда у вас есть несколько клиентских платформ — веб-браузеры, мобильные приложения, игровые консоли, устройства IoT — каждая с уникальные требования к данным, кэшированию, аутентификации и т. д.

BFF — это настраиваемый интерфейс (поддерживаемый фронтенд-командой), специально созданный для нужд каждой клиентской платформы и выступающий в качестве единственного «бэкенда» для клиента. Он находится между клиентом и базовыми источниками данных/микросервисами, сохраняя их развязку и предоставляя каждой клиентской команде полный контроль над тем, как они потребляют, консолидируют и массируют данные из нижестоящих зависимостей API, а также обрабатывают свои ошибки.

Но шаблон BFF, как и все остальное в технике, имеет свои недостатки и недостатки. Однако, как оказалось, GraphQL отлично справляется с устранением этих недостатков, и слой BFF, управляемый GraphQL, работает хорошо. Однако GraphQL приносит с собой свой собственный набор проблем!

Начинает звучать как заезженная пластинка. Как разорвать цикл? Давайте поговорим об этом, взглянув на бесплатную технологию с открытым исходным кодом — WunderGraph, — которая может нам помочь.

Бэкенды для интерфейсов ❤ GraphQL

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

1. BFF отлично работают в качестве уровня агрегации, но вам по-прежнему требуется несколько запросов для получения нужных данных, и это линейно масштабируется с количеством микросервисов/API/данных. зависимости у вас есть. Несколько сетевых переходов добавляют задержку.

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

2. Излишняя и неполная выборка могут по-прежнему быть проблемой из-за сочетания того, как работают архитектуры RESTful, и того факта, что вы можете полагаться на устаревшие или сторонние API, архитектуры которых вам не принадлежат.

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

GraphQL перекладывает эту ответственность на клиента, и он может указать именно те данные, которые ему нужны, в одном запросе.

Используют ли ваши подчиненные микросервисы/API сами GraphQL или нет, не имеет значения для взаимодействия клиента/BFF. Вам не нужно ничего переписывать, чтобы воспользоваться преимуществами.

3. Документация будет постоянной рутинной работой. BFF создается специально для конкретного клиента, поэтому для каждого BFF потребуется создать и поддерживать в актуальном состоянии подробную сопроводительную документацию по API (со спецификациями полезной нагрузки/ответа). При использовании REST имена конечных точек и глаголы HTTP будут только расти вместе с приложением и его сложностью, что затрудняет их документирование и, следовательно, трудность для новых сотрудников.

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

…но GraphQL не панацея.

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

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

2. GraphQL не имеет встроенного кэширования. Простота, которую приносит в таблицу формат единой конечной точки GraphQL, также может стать узким местом в определенных обстоятельствах — несколько конечных точек REST API позволяют им использовать кэширование HTTP, чтобы избежать перезагрузки ресурсов, но с GraphQL вы используете только одну универсальная конечная точка для всего, и для кэширования придется полагаться на внешние библиотеки (например, Apollo).

3. Клиенты GraphQL могут быть довольно тяжелыми с точки зрения размера пакета, который вы должны поставлять. Это может не иметь значения в некоторых случаях использования (например, если вы когда-либо создаете только внутреннее приложение), но это может и действительно влияет на время загрузки и производительность вашего приложения, особенно в сценариях с низкой пропускной способностью или медленными сетевыми соединениями. беспокойство.

4. Производительность может быть проблемой, если вы не будете осторожны. Природа GraphQL делает нетривиальной защиту вашей системы от попутных загрузок всей вашей базы данных. В GraphQL гораздо сложнее обеспечить соблюдение ограничений скорости, чем в REST, и один слишком сложный, слишком вложенный запрос, извлекающий слишком много данных, может привести к сканированию всего вашего бэкэнда. Даже случайно! Чтобы смягчить это, вам понадобится алгоритм для расчета стоимости запроса до получения каких-либо данных — и это дополнительная работа разработчиков. (Для справки: вот как это делает GraphQL API GitHub)

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

Представляем WunderGraph.

WunderGraph — это бесплатный фреймворк с открытым исходным кодом (лицензия Apache 2.0) для создания бэкендов для внешних интерфейсов.



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

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

Давайте начнем. Прежде всего, вам нужно назвать свои API и службы как зависимости данных, так же, как вы добавляете зависимости проекта в файл package.json.

./.wundergraph/wundergraph.config.ts

// Data dependency #1 - Internal database
const db = introspect.postgresql({
 apiNamespace: "db",
 databaseURL: "postgresql://myusername:mypassword@localhost:5432/postgres",
});

// Data dependency #2 - Third party API
const countries = introspect.graphql({
 apiNamespace: "countries",
 url: "https://countries.trevorblades.com/",
});

// Add both to dependency array
configureWunderGraphApplication({
 apis: [db, countries],
});

Затем вы можете писать операции GraphQL (запросы, мутации, подписки) для получения нужных данных из этого консолидированного виртуального графа.

./.wundergraph/operations/CountryByID.graphql

query CountryByID($ID: ID!){
 country: countries_country(code: $ID) {
 name
 capital
 }
}

./.wundergraph/операции/Пользователи.graphql

query Users($limit: Int!){
 users: db_findManyusers(take: $limit) {
 username
 email
 }
}

… и WunderGraph автоматически создаст конечные точки, которые вы можете указать для выполнения этих запросов и возврата нужных данных через JSON-RPC.

> $ curl http://localhost:9991/operations/CountryByID?ID=NZ
> {"data":{"country":{"name":"New Zealand","capital":"Wellington"}}}
> $ curl http://localhost:9991/operations/Users?limit=5
>{"data":{"users":[{"username":"user1","email":"[email protected]"},{"username":"user2","email":"[email protected]"},{"username":"user3","email":"[email protected]"},{"username":"user4","email":"[email protected]"},{"username":"user5","email":"[email protected]"}]}}

Если вы используете библиотеки для извлечения данных, такие как SWR или Tanstack Query, или фреймворк на основе React (NextJS, Remix и т. д.), вы получите еще больше от WunderGraph, поскольку он автоматически генерирует компактный, производительный, настраиваемый Клиент TypeScript для вас каждый раз, когда вы определяете операцию, в комплекте с полностью типобезопасными перехватчиками для запроса и изменения данных, которые вы можете использовать во внешнем интерфейсе, где бы вы ни нуждались.

./страницы/index.tsx

// Import data fetching hook from WunderGraph-generated client
import { useQuery } from "../components/generated/nextjs";

const Home: NextPage = () => {
 // Data dependency #1 - Internal Database
 const { data: usersData } = useQuery({
 operationName: "Users",
 input: {
 limit: 5,
 },
 });

 // Data dependency #2 - Third-party API
 const { data: countryData } = useQuery({
 operationName: "CountryByID",
 input: {
 ID: "NZ",
 },
 });
…
}

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

Но как WunderGraph решает проблемы GraphQL?

Легко — WunderGraph полностью выводит GraphQL из среды выполнения.

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

Не беспокойтесь, если это не сразу вам понравится. Я обещаю, что скоро это будет иметь смысл. Рассмотрим этот процесс подробно.

GraphQL только во время локальной разработки?

Всякий раз, когда вы пишете запрос, мутацию или подписку GraphQL и нажимаете «Сохранить», запрос НЕ сохраняется как есть. Вместо этого он хешируется, а затем сохраняется на сервере, и клиент может получить к нему доступ, только вызвав хешированное имя (конечно, вместе с входными переменными, если это необходимо).

Это так называемый постоянный запрос. Ваш сервер WunderGraph BFF имеет список известных хэшей — столько постоянных запросов, сколько вы определили — и будет толькоотвечать на клиентские запросы на действительные хэши, предоставляя их в виде JSON через RPC, с уникальной конечной точкой для каждого и каждую операцию. (И WunderGraph генерирует безопасные для типов хуки для фреймворков React и библиотек для выборки данных, которые делают то же самое)

Что это значит? Две вещи.

  1. Вы решаете проблемы безопасности, связанные с обратным проектированием вашего GraphQL API из сетевых запросов браузера, поскольку GraphQL API не существует.
  2. Если каждая операция имеет уникальную конечную точку, вы, наконец, можете реализовать традиционное кэширование в стиле REST! Но это не все. Сервер WunderGraph BFF генерирует Тег объекта (ETag) для каждого ответа — это уникальный идентификатор содержимого указанного ответа — и если клиент делает последующий запрос к той же конечной точке, и его ETag совпадает с текущим ETag на сервер, это означает, что содержимое не изменилось. Затем сервер может просто ответить HTTP 304 Не изменено, что означает, что кэшированная версия клиента все еще действительна. Это делает стратегии устаревающих при повторной проверке на стороне клиента невероятно быстрыми.

Вы можете писать любые произвольные запросы, используя GraphQL, во время разработки, но они хешируются и сохраняются во время компиляции, а во время выполнения отображаются только как JSON RPC. Одним махом вы решили наиболее распространенные проблемы с GraphQL, создав сценарий, сочетающий в себе лучшее из двух миров. Все преимущества GraphQL без каких-либо недостатков.

Но как насчет проблем с производительностью слишком сложных запросов?

WunderGraph использует Prisma ORM внутри, создавая оптимизированные запросы для каждой операции, которую вы пишете, независимо от источника данных. Вам не придется беспокоиться о том, чтобы вручную свернуть свои собственные запросы SQL или GraphQL и очистить их, чтобы убедиться, что один наивный запрос не уничтожит ваши нижестоящие службы или не приведет к DOS-атакам.

Куда пойти отсюда?

Вы видели, как WunderGraph значительно упрощает создание BFF, используя при этом фантастический devex. Используя Prisma под капотом, он создает постоянные запросы и обслуживает их через JSON-RPC, возвращая GraphQL к использованию только в локальной разработке.

Но вы можете сделать гораздо больше с WunderGraph — это настоящая структура BFF:

  • Он поддерживает полностью безопасную разработку от начала до конца. Каждая написанная вами операция будет генерировать типизированный клиент/хук для доступа к ней из внешнего интерфейса… и вы даже можете создавать безопасные макеты таким образом! С WunderGraph у вас есть общие типы между бэкэндом и внешним интерфейсом, что дает вам полное автозаполнение IDE и вывод как при написании запросов GraphQL, так и при разработке клиента.
  • Он может превратить любой написанный вами запрос в Живой запрос даже без использования веб-сокетов, что делает WunderGraph идеальным для создания BFF, которые развертываются вместе с интерфейсом на бессерверных платформах, которые не могут использовать веб-сокеты.
  • Он также может интегрировать S3-совместимое хранилище для загрузки файлов, поставщиков аутентификации, совместимых с OIDC / не-OIDC, и многое другое.
  • Если вы вообще не хотите писать GraphQL, вы можете написать собственные функции TypeScript — по сути, асинхронные преобразователи — для определения операций во время разработки. Вы можете выполнять в них проверку схемы JSON с помощью Zod, а также агрегировать, обрабатывать и массировать данные любым удобным для вас способом, а затем возвращать их клиенту. Доступ к операциям TypeScript осуществляется точно так же, как к операциям GraphQL (конечные точки JSON RPC или перехватчики на стороне клиента), и они полностью выполняются на сервере и никогда не отображаются на стороне клиента. Кроме того, вы даже можете импортировать и использовать в них существующие операции GraphQL.
  • Вы можете запрограммировать сквозные проблемы, которые идеально присутствуют на уровне BFF — и, что наиболее важно, аутентификацию — в слой BFF WunderGraph через Clerk/Auth.js/ваше собственное решение аутентификации, и каждая операция может быть осведомлена об аутентификации.

Благодаря мощному самоанализу, который превращает API, базу данных и микросервисы в зависимости, как это делает менеджер пакетов, такой как NPM, WunderGraph делает разработку BFF доступной и приятной, никогда не ставя под угрозу сквозную безопасность типов. Чтобы узнать больше, ознакомьтесь с их документацией здесь и их сообществом Discord здесь.