Всем привет, меня зовут Сергей Прощаев, и в этой статье расскажу про то, что обычно остаётся за кадром для многих бэкенд‑разработчиков — про «вторую половину» API.

Много лет пишу на Java и Kotlin, проектирую высоконагруженные системы и, честно говоря, долгое время относился к фронтенду немного свысока. Ну подумаешь, дёрнул наш эндпоинт, получил JSON и отрисовал. Что там сложного? Пока однажды не пришлось плотно разбираться с производительностью одного внутреннего портала. Бэкенд летал, база данных отвечала за миллисекунды, а пользователи всё равно жаловались на «тормоза». Проблема, как выяснилось, была на стороне клиента: он дёргал сервер десятками лишних запросов, кеширование было настроено «на отвали», а данные приходили в таком виде, что фронтенду приходилось их дополнительно склеивать и трансформировать, тратя драгоценные такты процессора пользовательского ноутбука.

Тогда я и понял простую истину: API — это не просто контракт между сервисами, это язык общения, который обязан быть удобен для обеих сторон. Особенно для той, которая находится ближе всего к пользователю. Если фронтенду неудобно работать с вашими данными, он найдёт способ сделать им удобно, и этот способ часто идёт вразрез с архитектурой и производительностью.

Сегодня я хочу примерить на себя роль «переводчика» с бэкенд‑на‑фронтенд и разобрать, как же современный JavaScript‑фронтенд общается с сервером. Мы не будем писать код, а посмотрим на это глазами архитектора. Поговорим об эволюции подходов, лучших практиках гигантов вроде Netflix и Shopify, и о том, почему умение правильно организовать этот «диалог» — маркер крутого специалиста.

От "Толстого" Бэкенда к "Умному" Клиенту: Эволюция паттернов

Ещё лет 10 назад доминировала классическая модель: сервер рендерил HTML и отдавал готовые страницы. Фронтенд был «тонким» и «глупым». Всё изменилось с приходом SPA (Single Page Application). Фронтенд отделился от бэкенда, став самостоятельным приложением, которое живёт в браузере пользователя. И тут же встал вопрос: как этому приложению получать данные?

Первой ласточкой стал, конечно, REST (Representational State Transfer). Простой, понятный, основанный на HTTP‑методах и ресурсах. Бэкендеры его обожали (и до сих пор обожают), потому что он идеально ложился на CRUD‑операции в базах данных.

Но чем сложнее становились интерфейсы, тем отчётливее проступали «швы» REST‑подхода. Представьте страницу профиля пользователя в условном маркетплейсе. Нам нужно показать: имя пользователя, его аватар, список последних заказов, корзину и рекомендованные товары. На классическом REST‑бэкенде это выльется минимум в 5 отдельных запросов:

  1. GET /api/users/{id}

  2. GET /api/users/{id}/avatar

  3. GET /api/users/{id}/orders?limit=5

  4. GET /api/cart

  5. GET /api/recommendations

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

GraphQL: Революция вопросов и ответов

Именно для решения проблемы over‑fetching (получения лишних данных) и under‑fetching (недостаточности данных в одном ответе) в 2015 году Facebook представил GraphQL.

Это не просто технология, это смена парадигмы. Вместо множества эндпоинтов, у нас есть одна точка входа. Клиент сам отправляет запрос (query), в котором чётко указывает, какие поля и какие связи ему нужны.

Давайте посмотрим, как это выглядит на схеме

Для этого можно воспользоваться Mermaid и нарисовать такую диаграмму, которая представлена на рис. 1.

Рис. 1. Сравнение REST и GraphQL при запросе составных данных
Рис. 1. Сравнение REST и GraphQL при запросе составных данных

Что мы видим? Вместо двух кругов поездок туда‑обратно — один. Вместо лишних данных — только то, что попросили. Но, как и л��бой мощный инструмент, GraphQL требует аккуратного обращения. Если на REST мы можем повесить rate limit на каждый эндпоинт, то здесь один сложный запрос может лечь на базу данных не хуже, чем полноценный DDoS. Именно поэтому в крупных компаниях (например, GitHub, Shopify) внедрение GraphQL идёт рука об руку с анализом сложности запросов (query complexity analysis) и строгими лимитами.

BFF (Backend for Frontend): У каждого свой язык

Однако GraphQL решил не все проблемы. Оставался вопрос: а что, если у нас несколько клиентов? Веб‑версия, мобильное приложение (iOS/Android), умные часы? Их потребности в данных кардинально разнятся. Мобильному приложению важнее трафик и размер payload'а, вебу — скорость первого экрана, часам — минимализм.

В 2015 году, параллельно с расцветом микросервисов, команда SoundCloud поделилась своим паттерном Backend for Frontend (BFF). Суть гениально проста: вместо одного «универсального» API для всех, мы создаём отдельный слой (прослойку) для каждого типа клиента.

BFF — это обычно легковесный сервис (часто на Node.js, чтобы фронтенд‑разработчики могли его поддерживать), который:

  1. Принимает запрос от конкретного клиента.

  2. Ходит в нужные микросервисы (или GraphQL‑федерацию).

  3. Агрегирует, обогащает и «нарезает» данные идеально для этого клиента.

  4. Отдаёт готовый результат.

Это даёт невероятную гибкость. Можно отдавать мобильному приложению данные в MessagePack вместо JSON для экономии трафика. Можно для веба подклеивать дополнительные метада��ные для SEO, которые не нужны в приложении.

Еще можно привести пример Netflix. Про это много написано статей. У них огромная микросервисная архитектура. Представьте, что телевизор (клиент Netflix на Smart TV) запрашивает главную страницу. Ему нужно собрать данные из десятков сервисов (рекомендации, мои список, тренды, новинки, трейлеры). Если бы телевизор делал это напрямую, это был бы ад. Вместо этого запрос уходит в BFF для TV. Этот BFF заточен под особенности платформы: он знает, что телевизор — это «глупое» устройство с ограниченной памятью, которому лучше отдать уже готовый, идеально структурированный JSON за один раз, чтобы интерфейс не «лагал». Они называют этот слой «Falcor» (хотя это их собственная разработка, концептуально очень близкая к BFF). Это позволило им радикально ускорить разработку под каждое устройство, не ломая голову над тем, как адаптировать общий API под все возможные сценарии.«»

Real-time и "Реактивное" состояние: Лучше спросить один раз

Следующий шаг эволюции — переход от синхронных запросов к реактивным подходам. Пользователи хотят видеть изменения в реальном времени (как в Figma или Google Docs). А разработчики хотят писать меньше кода для обработки состояний загрузки, ошибок и обновлений.

Здесь на сцену выходят две мощные концепции:

  1. WebSockets, Server‑Sent Events (SSE) и gRPC‑streaming: Технологии, позволяющие серверу «пушить» данные на клиент. Биржевые котировки, уведомления, сообщения в чате — всё это уже не дёргает сервер вопросом «а что там новенького?», а получает данные сразу, как только они появились.

  2. Управление состоянием сервера (Server‑State Management): Это то, о чём мало говорят на старте, но это краеугольный камень любого сложного фронтенда. Библиотеки вроде TanStack Query (React Query), RTK Query, Apollo Client совершили революцию.

Раньше разработчики тащили данные с сервера в глобальный стор (например, Redux) и вручную городили огороды из флагов isLoadingerrordata. Это был бесконечный источник багов. «Умные» библиотеки предложили другой подход: они берут на себя всю работу с асинхронным состоянием.

Когда впервые увидел React Query, был поражён. Эта штука делает для данных на клиенте то же, что Hibernate делает для базы данных на сервере — скрывает сложность управления состоянием и синхронизацией. Фронтенд‑разработчику теперь не нужно думать о том, когда обновить данные после отправки формы. Он просто говорит: «После мутации addTodo сделай недействительным (invalidate) запрос todos„. Библиотека сама сходит за новыми данными в фоне. Или даже оптимистично обновит интерфейс, а если запрос упадёт — откатит изменения назад. Это уровень комфорта, к которому мы все должны стремиться.“»

Где живут лучшие практик��?

Часто спрашивают: а где искать эти самые лучшие практики, о которых вы говорите? Смотреть на код приложений в открытых репозиториях? Не всегда. Крутые инженерные решения часто спрятаны в инфраструктурных проектах.

Один из моих любимых примеров — это то, как команда Vercel (создатели Next.js) развивает идеи, которые потом становятся стандартом де‑факто.

Возьмём концепцию Server Components в React. Это гениальный гибрид, который размывает грань между бэкендом и фронтендом. Компонент может быть асинхронным и выполняться прямо на сервере, обращаясь к базе данных или API, а клиент получает уже готовый HTML и минимальный JavaScript для интерактивности. Это не просто паттерн, это фундаментальное изменение парадигмы, которое делает приложения быстрее, а код — проще. Они не просто сделали удобную библиотеку, они перепридумали способ доставки контента.

Или подход «Partial Prerendering» (частичная предварительная генерация). Они задались вопросом: «Почему страница должна быть либо полностью статической (быстро, но неактуально), либо полностью динамической (актуально, но медленно)?» Их решение — комбинировать: отдавать пользователю мгновенный статический скелет страницы, а динамические части (например, корзину пользователя) подгружать параллельно стримингом. Это требует сложной оркестрации на сервере и на клиенте, но даёт потрясающий пользовательский опыт.

И таких примеров масса. Shopify придумали Hydrogen — фреймворк для e‑commerce, где данные о товарах зашиваются прямо в код страницы на этапе сборки, но при этом остаётся возможность для персонализации. Это всё не магия, это результат глубокого анализа того, как и какие данные нужны фронтенду.

Заключение: Мост через пропасть

Так зачем же всё это знать бэкенд‑разработчику, который пишет на Java? А затем, что современное приложение — это система, и её слабое звено — это интерфейс между бэкендом и фронтендом. Если вы проектируете API, не думая о том, как его будут потреблять, — вы создаёте проблему.

  • Вы спроектируете REST‑ресурс «документы», и фронтенду придётся делать 10 запросов для страницы со списком документов и их авторами.

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

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

Хороший системный аналитик или архитектор (и, кстати, сильный бэкендер) всегда смотрит на задачу шире. Он понимает, что JSON, который он отдаёт, — это не просто данные, это топливо для сложной машины под названием «фронтенд‑приложение». И от качества этого топлива зависит, поедет эта машина быстро или встанет колом на полпути.

И если вы чувствуете, что хотите копнуть эту тему глубже, понять, как живёт и дышит современный JavaScript, и научиться выстраивать эти мосты правильно, а не на коленке — приглашаю вас на открытый урок курса «Разработчик на JavaScript. Уровень Про», который состоится 11 марта в 19:00. Участие бесплатное, нужно записаться.

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