Мокирование Fetch API

Workbox предоставляет мощный инструмент для работы с Service Worker и управлением кэшированием ресурсов на клиентской стороне. Одной из ключевых возможностей является перехват и мокирование запросов Fetch, что позволяет создавать гибкие стратегии кэширования и тестирования сетевых запросов без изменения серверного кода.


Перехват запросов с workbox-routing

Для начала работы с мокированием запросов используется модуль workbox-routing. Он позволяет определить правила маршрутизации и сопоставления URL-запросов с обработчиками.

Пример базового перехвата всех GET-запросов к API:

import { registerRoute } from 'workbox-routing';
import { NetworkFirst } from 'workbox-strategies';

registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst()
);

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


Использование кастомного обработчика для мокирования

Workbox позволяет создавать кастомные обработчики, возвращающие статические данные или динамически формируемый ответ вместо обращения к реальному серверу.

Пример мокирования запроса к /api/user:

import { registerRoute } from 'workbox-routing';

registerRoute(
  ({ url }) => url.pathname === '/api/user',
  async () => {
    return new Response(JSON.stringify({
      id: 1,
      name: 'John Doe',
      role: 'admin'
    }), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
);

Здесь при любом запросе к /api/user клиент всегда получит заранее определённый JSON, что полезно для разработки и тестирования интерфейсов без задействования реального API.


Комбинация мокирования с кэшированием

Часто возникает необходимость сочетать мокирование с кэшированием. Workbox позволяет реализовать гибридные стратегии, например, отдавать мокированные данные при оффлайне, а при наличии сети обновлять кэш.

import { CacheFirst } from 'workbox-strategies';

registerRoute(
  ({ url }) => url.pathname.startsWith('/api/products'),
  async ({ event }) => {
    try {
      const response = await fetch(event.request);
      return response;
    } catch (error) {
      return new Response(JSON.stringify([
        { id: 101, name: 'Product A' },
        { id: 102, name: 'Product B' }
      ]), { headers: { 'Content-Type': 'application/json' } });
    }
  }
);

В этом примере сначала пробуется сетевой запрос, и только при его неудаче возвращается мокированный ответ. Такой подход облегчает разработку offline-first приложений.


Применение workbox-precaching для статических моков

Иногда необходимо заранее закэшировать набор статических JSON-файлов, чтобы мокирование было полностью автономным. Для этого используется workbox-precaching:

import { precacheAndRoute } from 'workbox-precaching';

precacheAndRoute([
  { url: '/mock/users.json', revision: '1' },
  { url: '/mock/products.json', revision: '1' },
]);

registerRoute(
  ({ url }) => url.pathname.endsWith('/api/users'),
  async () => {
    return caches.match('/mock/users.json');
  }
);

Такой подход позволяет полностью контролировать данные на клиентской стороне без сетевых зависимостей.


Мокирование с динамическими параметрами запроса

Workbox позволяет обрабатывать GET-запросы с query-параметрами. Для этого можно анализировать URL и формировать ответы динамически:

registerRoute(
  ({ url }) => url.pathname === '/api/search',
  ({ url }) => {
    const query = url.searchParams.get('q');
    const results = query ? [`Result for "${query}"`] : [];
    return new Response(JSON.stringify(results), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
);

Такой подход позволяет имитировать поведение API при поисковых запросах, полностью на клиентской стороне, без обращения к серверу.


Отладка моков и логирование

Для упрощения разработки полезно логировать все перехваченные запросы:

registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  async ({ request }) => {
    console.log('Intercepted request:', request.url);
    return new Response(JSON.stringify({ mocked: true }), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
);

Логи помогают следить за тем, какие запросы мокируются и корректно ли обрабатываются сценарии offline и online.


Рекомендации по архитектуре мокирования

  • Мокирование следует изолировать в отдельный файл Service Worker или модуль, чтобы не смешивать реальные и тестовые маршруты.
  • Для production лучше отключать мокирование через условие, проверяющее переменные окружения.
  • Кэшированные и мокированные данные должны иметь корректные заголовки Content-Type и Cache-Control, чтобы не возникало конфликтов с реальными ответами сервера.
  • При использовании динамических моков стоит ограничивать размер данных, чтобы не перегружать память клиента.