Реализация BFF (Backend for Frontend) паттерна

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация BFF (Backend for Frontend) паттерна
Сложная
~2-4 недели
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Реализация BFF (Backend for Frontend) паттерна

BFF — паттерн, при котором для каждого типа клиента (web, mobile, TV-приложение) создаётся отдельный бэкенд-слой. Вместо одного универсального API, которое пытается угодить всем клиентам сразу, каждый BFF возвращает данные в форме, оптимальной для своего клиента.

Проблема без BFF

Мобильное приложение делает 5 запросов чтобы показать один экран профиля:

  • GET /users/{id} — базовые данные
  • GET /orders?userId={id}&limit=3 — последние заказы
  • GET /notifications/unread — счётчик уведомлений
  • GET /recommendations?userId={id} — рекомендации
  • GET /loyalty/points/{id} — баллы лояльности

Каждый запрос — отдельный round trip. На мобильной сети это 500–1500ms суммарно.

Решение с BFF

                    Mobile BFF          Web BFF
                    (Node.js)           (Node.js)
iOS App ──────────► /mobile/dashboard   │
Android App ───────► │                  │
                    │                  │
                    ├─► User Service   ◄─── Web SPA
                    ├─► Order Service  ◄──────────
                    ├─► Notification   ◄──────────
                    └─► Recommendation ◄──────────

Реализация Mobile BFF

// mobile-bff/routes/dashboard.ts
router.get('/mobile/dashboard', authenticate, async (req, res) => {
  const userId = req.user.id;

  // Параллельные запросы к микросервисам
  const [userResult, ordersResult, notificationsResult, loyaltyResult] =
    await Promise.allSettled([
      userService.get(`/users/${userId}`),
      orderService.get(`/orders?customerId=${userId}&limit=3&fields=id,status,total,createdAt`),
      notificationService.get(`/notifications/${userId}/unread-count`),
      loyaltyService.get(`/loyalty/${userId}/summary`)
    ]);

  // Агрегация с обработкой partial failures
  const response = {
    user: userResult.status === 'fulfilled' ? {
      id: userResult.value.data.id,
      name: userResult.value.data.displayName,
      avatar: userResult.value.data.avatarUrl
    } : null,

    recentOrders: ordersResult.status === 'fulfilled'
      ? ordersResult.value.data.items.map(transformOrderForMobile)
      : [],

    unreadCount: notificationsResult.status === 'fulfilled'
      ? notificationsResult.value.data.count
      : 0,

    loyalty: loyaltyResult.status === 'fulfilled' ? {
      points: loyaltyResult.value.data.balance,
      tier: loyaltyResult.value.data.tier
    } : null
  };

  res.json(response);
});

// Трансформация данных под мобильный UI
function transformOrderForMobile(order: Order): MobileOrder {
  return {
    id: order.id,
    status: localizeStatus(order.status),  // 'Доставлен' вместо 'DELIVERED'
    total: formatCurrency(order.total, 'RUB'),
    date: formatRelativeDate(order.createdAt)  // '2 дня назад'
  };
}

Web BFF — другой формат для тех же данных

// web-bff/routes/dashboard.ts
router.get('/web/dashboard', authenticate, async (req, res) => {
  const userId = req.user.id;

  // Web-версия запрашивает больше данных для богатого UI
  const [user, orders, stats, notifications] = await Promise.allSettled([
    userService.get(`/users/${userId}`),
    orderService.get(`/orders?customerId=${userId}&limit=10`),
    analyticsService.get(`/analytics/user/${userId}/stats`),
    notificationService.get(`/notifications/${userId}?limit=5&unread=true`)
  ]);

  // Web-формат — больше данных, другая структура
  res.json({
    user: user.status === 'fulfilled' ? user.value.data : null,
    orders: orders.status === 'fulfilled' ? orders.value.data : { items: [], total: 0 },
    analytics: stats.status === 'fulfilled' ? stats.value.data : null,
    notifications: notifications.status === 'fulfilled' ? notifications.value.data : []
  });
});

GraphQL BFF

Если клиент — React-приложение с Apollo Client, BFF может экспортировать GraphQL:

// web-bff/graphql/schema.ts
const typeDefs = gql`
  type Query {
    dashboard: Dashboard!
    order(id: ID!): Order
  }

  type Dashboard {
    user: User!
    recentOrders: [Order!]!
    stats: UserStats!
  }
`;

const resolvers = {
  Query: {
    dashboard: async (_, __, { userId }) => {
      const [user, orders, stats] = await Promise.all([
        userService.getUser(userId),
        orderService.getRecentOrders(userId),
        analyticsService.getUserStats(userId)
      ]);
      return { user, recentOrders: orders, stats };
    }
  }
};

Авторизация и аутентификация в BFF

BFF — естественное место для проверки JWT и управления сессиями. Особенно для браузерного клиента:

// BFF хранит refresh token в httpOnly cookie,
// не передавая его в браузер JavaScript
router.post('/auth/refresh', async (req, res) => {
  const refreshToken = req.cookies.refresh_token;
  if (!refreshToken) return res.status(401).json({ error: 'No token' });

  const tokens = await authService.refreshTokens(refreshToken);

  res.cookie('refresh_token', tokens.refreshToken, {
    httpOnly: true, secure: true, sameSite: 'strict',
    maxAge: 30 * 24 * 60 * 60 * 1000  // 30 дней
  });

  res.json({ accessToken: tokens.accessToken });
});

Кеширование в BFF

import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);

async function getCachedOrFetch<T>(
  key: string,
  ttl: number,
  fetcher: () => Promise<T>
): Promise<T> {
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);

  const data = await fetcher();
  await redis.setex(key, ttl, JSON.stringify(data));
  return data;
}

// Рекомендации кешируем на 5 минут
const recommendations = await getCachedOrFetch(
  `recommendations:${userId}`,
  300,
  () => recommendationService.get(`/recommendations/${userId}`)
);

Сроки реализации

  • BFF для одного клиента (3–5 агрегирующих эндпоинтов) — 1–2 недели
  • GraphQL BFF + авторизация + кеширование — 2–3 недели
  • BFF для 2–3 клиентов с общей библиотекой вызовов сервисов — 3–4 недели