Понедельник, утро, открываешь проект, а авторизация на dev-стенде снова сломана. Или же надо поторопиться с релизом фичи, а бэк еще не готов, и разрабатывать нужно параллельно, и тут без моков не обойтись. Ну или классика: в командировке лежит интернет, и вся работа встала колом. Знакомо?
Что тут можно поделать? Можно подождать, пока починят стенд. Можно залезть в код и что-нибудь там нахардкодить, переполнив его ненужной логикой моков. Можно поставить Postman, Insomnia или другие популярные решения. Но у каждого подхода есть подводные камни: простои и сорванные дедлайны, забытые хардкоды в продакшене, необходимость согласований с инфобезом (особенно если вы работаете в банке) и зависимость от внешних серверов.
Мы попробовали существующие решения и поняли: ни одно из них не закрывает наши потребности полностью. Нужно было что-то свое: простое в установке, работающее офлайн, не требующее дополнительных приложений и аккаунтов. Что-то, что можно быстро настроить под себя и не зависеть от внешних решений вендора. Так появился наш велосипед — браузерное расширение Req-Saver.
Да, представимся. Мы — Александр Битько и Дмитрий Панфилов, фронтенд-разработчики в ПСБ. Сегодня расскажем, как превратили мокирование запросов из головной боли в простую и понятную работу.
Что предлагает рынок: популярные решения и подводные камни
Прежде чем изобретать велосипед, мы изучили, что уже существует. Инструменты есть, но у каждого свои проблемы.
Chromium Overrides — встроенная фича Chrome и Яндекс Браузера. Казалось бы, идеально: ничего устанавливать не нужно, открыл DevTools и мокай запросы. Но, как оказалось, этим не очень удобно пользоваться. Чтобы создать правило, нужно сначала поймать живой запрос, а потом уже от него плясать. Вдобавок никакого импорта из OpenAPI, все правила только руками.

Postman с его Mock Server — это уже довольно зрелый продукт. У него удобный редактор, импорт схем, множество настроек. Но есть нюанс: нужно качать отдельное приложение, поднимать мок-сервер, заводить аккаунт. И самое неприятное — моки работают только на серверах Postman, так что в офлайне все равно ничего не работает.

Insomnia — та же история, только с более современным интерфейсом и своими багами при импорте OpenAPI. В платной версии можно поднять сервер локально, но опять же — отдельное приложение.

Mockoon предлагает бесплатный локальный запуск, что уже лучше. Но суть та же: требуется отдельное приложение.

Плюс, пытаясь использовать сторонние решения в банке, мы столкнулись с тем, что часть приложений нам попросту недоступна либо функционал сильно урезан и это не покрывает наших потребностей. Стало понятно, что нам нужно что-то более простое и гибкое. И желательно свое.
Браузерное расширение Req-Saver
В итоге мы пошли своим путем: никаких отдельных приложений и аккаунтов, просто расширение для браузера. Скачал архив, распаковал, добавил в Яндекс Браузер или Chrome — и все работает.

Под капотом проект собран на React с UI-фреймворком Material-UI — получается знакомый и понятный интерфейс. Для сборки используем Vite, потому что у него есть готовый плагин для горячей перезагрузки при разработке расширений. Это серьезно экономит время разработки.
Самое интересное — то, как мы перехватываем запросы. Используем встроенный Chrome DevTools Protocol (CDP), который позволяет активировать режим отладчика и перехватывать все HTTP-запросы на лету. А для редактирования JSON-ответов встроили Monaco Editor — тот самый движок, что работает в VS Code. Так что, если вы привыкли к Visual Studio Code, здесь будете чувствовать себя как дома.
Пример кода по обработке перехваченного запроса:
Debugger.Fetch.Events.RequestPaused.subscribe(async (eventData) => {
let rule: IOverrideRule | null = null;
try {
rule = ruleMatcher.matchRule(eventData);
} catch {
console.warn("Не удалось найти правило");
}
if (rule) {
const requestMutator = new RequestMutator();
const mutableRequest = MutableRequest.fromEvent(eventData);
requestMutator.mutate(mutableRequest, rule);
const fullRequest = mutableRequest.build();
if (
fullRequest.response ||
fullRequest.statusCode ||
fullRequest.statusPhrase
) {
const fulfillRequest: Protocol.Fetch.FulfillRequestRequest = {
requestId: fullRequest.id,
body: fullRequest.response,
responseCode: fullRequest.statusCode ?? 200,
responsePhrase: fullRequest.statusPhrase || "OK",
};
if (eventData.responseStatusCode === undefined) {
Debugger.Fetch.Methods.FulfillRequest.send(fulfillRequest);
} else {
Debugger.Fetch.Methods.ContinueResponse.send(fulfillRequest);
}
return;
}
const requestHeaders: Protocol.Fetch.HeaderEntry[] = Object.entries(
fullRequest.requestHeaders
).map((entry) => ({ name: entry[0], value: entry[1] }));
Debugger.Fetch.Methods.ContinueRequest.send({
requestId: fullRequest.id,
headers: requestHeaders,
});
return;
}
const isResponse = eventData.responseStatusCode !== undefined;
if (isResponse) {
Debugger.Fetch.Methods.ContinueResponse.send({
requestId: eventData.requestId,
});
} else {
Debugger.Fetch.Methods.ContinueRequest.send({
requestId: eventData.requestId,
});
}
});
Идея простая: установил за 30 секунд и работаешь сразу. Весь код лежит на GitLab, легко поддается модификации под нужды команды. Полный контроль над решением означает, что мы можем быстро добавлять фичи, которые реально нужны разработчикам, а не гнаться за универсальностью.
Дополнительно мы сделали импорт и экспорт настроек, запросов и тому подобного в JSON, чтобы все, что нужно, можно перекинуть коллеге или использовать как бэкап.
Немного подробностей технической реализации
Что такое Chrome DevTools Protocol
Chrome DevTools Protocol (CDP) — это низкоуровневый протокол взаимодействия с движком Chromium и браузерами на его основе (Chrome, Edge, Electron и т.д.). По сути это JSON-RPC поверх WebSocket или pipe-соединения, которое позволяет напрямую управлять внутренними процессами браузера: от сетевых запросов и исполнения JavaScript до работы с DOM, рендерингом и профилированием.
Протокол построен вокруг доменов (Domains), которые группируют команды и события по областям функциональности. Например:
Network — перехват и модификация HTTP-запросов/ответов;
Runtime — исполнение JavaScript-кода в контексте страницы;
Debugger — управление точками останова, пошаговая отладка;
Fetch — более высокий уровень перехвата запросов с возможностью модификации;
Page, DOM, CSS, Profiler, Log и многие другие.
Каждый домен предоставляет:
методы (Methods) — команды, которые можно отправить браузеру (например, Fetch.continueRequest, Network.enable, Runtime.evaluate);
события (Events) — уведомления от браузера о происходящих изменениях (например, Fetch.requestPaused, Debugger.paused, Network.responseReceived).
Принцип работы CDP
Клиент (например, наше расширение или отдельный инструмент) открывает соединение с браузером через WebSocket.
Для каждого действия формируется JSON-запрос с уникальным id, именем метода (method) и аргументами (params).
{ "id": 1, "method": "Fetch.enable", "params": { "patterns": [{ "urlPattern": "*" }] } }
Браузер возвращает результат выполнения команды с этим же id.
{ "id": 1, "result": {} }
Независимо от команд браузер может отправлять события, на которые клиент подписан.
{ "method": "Fetch.requestPaused", "params": { "requestId": "1234", "request": { "url": "https://example.com", "headers": {} } } }
Основной функционал, используемый в нашем случае
Для работы с отладчиком был создан отдельный класс Debugger, который в себе хранит информацию о всех поддерживаемых методах и событиях, разделяя их на домены и позволяя легко обращаться к каждому методу и событию. Например:
Debugger.Fetch.Events.RequestPaused.subscribe(callback) // Подписаться на событие Fetch.requestPaused
Debugger.Fetch.Methods.ContinueRequest.send(payload) // Отправить команду Fetch.continueRequest
Так у нас сохраняется вся мощь типизации и удобство работы с отладчиком.
Механизм отлова запросов использует событие Fetch.requestPaused, которое позволяет получить всю необходимую информацию о входящем запросе (URL, заголовки, тело). Далее запрос можно:
модифицировать (например, подменить тело ответа, статус или заголовки);
продолжить без изменений (Fetch.continueRequest);
заблокировать или заменить другим ответом (Fetch.fulfillRequest).
Перехват происходит путем перебора каждого правила и списка критериев. Если запрос соответствует одному из правил, запускается цепочка модификаторов:
модификатор тела ответа;
модификатор статуса ответа;
модификатор заголовков.
Каждый модификатор вносит свои изменения, и на выходе формируется уже новый вариант запроса/ответа, который возвращается на фронт.
Взаимодействие фронта и воркера
Чтобы обеспечить коммуникацию между воркером расширения и фронтовой частью, используется глобальный объект chrome.runtime, доступный как в браузерном контексте, так и в сервис-воркере. Общение строится по классической модели:
фронт отправляет сообщение через chrome.runtime.sendMessage;
воркер слушает событие через chrome.runtime.onMessage.addListener;
при необходимости воркер отправляет ответ обратно.
Таким образом, мы получаем полную цепочку:

Три сценария использования: от простого к сложному
В реальной работе у нас возникают разные кейсы для мокирования запросов. Мы выделили три основных, под которые и заточили интерфейс.
1. Бэкенд еще в разработке
Классика: дизайн готов, фронтенд почти написан, а API еще только в планах. Раньше приходилось либо ждать бэкендеров, либо хардкодить данные прямо в компонентах.
В Req-Saver создаешь правило с нуля, настраивая критерии: указываешь метод (поддерживается GET, POST, DELETE, PATCH, OPTIONS, PUT, HEAD), путь к эндпоинту. В редакторе JSON набиваешь нужный ответ, выставляешь статус — и вуаля, фронтенд думает, что работает с настоящим API. Можно спокойно разрабатывать и тестировать логику, не дожидаясь готового бэкенда.
2. Тестируем edge-случаи
А вот этот кейс посложнее. Допустим, нужно проверить несколько путей пользователя: основной флоу работает, но что будет при ошибке 500? А если API вернет пустой массив? А если в ответе придут некорректные данные?
Тут удобнее не создавать правила с нуля, а работать с готовыми. В Req-Saver включаешь режим записи, проходишь обычный сценарий — все правила автоматически сохраняются. Потом берешь любое записанное правило и модифицируешь: меняешь статус на 500, очищаешь массив данных, ломаешь JSON. Мы предусмотрели удобный интерфейс, позволяющий формировать различные группы под отладку различных кейсов и при разработке удобно в онлайн-режиме подмены запросов переключаться между ними.

Переключаешься между вариантами одним кликом и смотришь, как ведет себя интерфейс.
3. Сломанная инфраструктура / умершая сеть
Самый практичный кейс из всех. Допустим, авторизация на стенде упала, а работать нужно прямо сейчас. Или интернет пропал, а дедлайн горит.
При рабочей авторизации записываешь весь пользовательский путь — обычно это порядка 60 запросов от логина до основной работы. Когда что-то ломается, включаешь режим подмены, выбираешь записанную группу — и авторизация проходит по сохраненным ответам. Дальше проходишь свой конечный кейс.
Пользовательский интерфейс
Следуя за задачами, визуальный интерфейс мы разделили на две логические части: левая панель — управление правилами, группами, переключение режимов записи и подмены; правая — детальная работа с конкретным правилом и его кастомизация. Никаких лишних вкладок и меню, все под рукой.

Основные критерии для перехвата запросов, как уже говорилось выше, — это метод (GET, POST, DELETE, PATCH, OPTIONS, PUT, HEAD), сам путь к эндпоинту, плюс можно добавлять query-параметры вроде фильтров или поиска. Еще один полезный критерий — статус ответа. Это позволяет очень точно настроить, какие именно запросы нужно перехватывать, а какие пусть идут как обычно. Когда работаешь в режиме подмены, видишь рядом с каждым правилом циферку — сколько раз он сработал. Кажется мелочью, но на практике очень полезно: сразу видно, какие моки реально используются в твоем сценарии, а какие висят мертвым грузом.

Добавлены автоматические проверки на дублирование одинаковых правил внутри группы, что помогает избежать путаницы.
Да, немного о группах: представьте, вы работаете с несколькими приложениями одновременно. Без группировки все правила сваливаются в одну кучу — получается мусор, в котором ничего не найдешь. А так создаешь группу для каждого проекта, записываешь туда соответствующие правила — и порядок. Когда переключаешься на режим подмены, можно комбинировать правила из нескольких групп. Допустим, есть группа с основным флоу и группа с error-кейсами, выбираешь нужные правила из обеих и получаешь идеальную настройку для конкретной задачи.
Для работы с JSON реализован полноценный редактор с подсветкой синтаксиса — можно копипастить готовые объекты или писать с нуля. Статус ответа меняется простым выбором из списка, особенно это удобно при тестировании error-кейсов.

С заголовками ответа можно работать в двух режимах: либо через удобную таблицу с галочками (какие хедеры подменяем, какие — оставляем), либо переключиться в текстовый режим и редактировать как обычный список. Это пригождается, когда в заголовках хранятся сессии или токены авторизации.
Еще мы добавили импорт OpenAPI-схемы, которая позволяет создавать большое количество правил всего в пару кликов. Загружаешь файл с описанием API (тот же swagger) и автоматически получаешь заготовки для всех эндпоинтов проекта, а тестовые данные автоматически генерируются с помощью библиотеки faker.js. Это серьезно ускоряет первоначальную настройку моков.

Планы развития
Сейчас у нас есть работающий MVP (да, несмотря на приличный функционал, мы все еще считаем его MVP), но впереди много интересного. Из планов по развитию: добавить новые возможности для перехвата запросов — использование переменных и локальных сниппетов. Переменные позволят делать ответы более динамичными, а сниппеты — выполнять небольшие куски вашего кода, давая вам полную свободу при подмене.
Вместо заключения
Что же в результате? Мы довольны тем, что не зависим от решения вендора и можем дорабатывать и кастомизировать инструмент под нужды команды. Уточню, что Req-Saver стал удобным инструментом не только для разработчиков, но и для тестировщиков.
Возможность записи запросов на практике оказалась критически важной. Не все конкуренты предоставляют эту фишку, а она реально нужна. Вспомните кейс с авторизацией — 60 запросов, представьте, если бы их пришлось описывать руками.
И конечно, мы постарались сделать решение удобным. Гибкий user-friendly интерфейс, переключение между правилами, подмена только нужных запросов, комбинирование в единые группы под разные задачи — все это работает уже сейчас.
Рады были поделиться. Если возникли вопросы по технической реализации, задавайте в комментариях.