Главная/Кейсы/Macrulez REST API
Node.js · ExpressVue 3PostgreSQLEChartsDockerПет-проект

Admin-панель
для REST API сервиса

Жил с картиной «сервис работает — значит всё ок». Проблемы обнаруживались по жалобам или через docker logs | grep. Сделал полную наблюдаемость: дашборд, кэш с графиками, логи, аналитика, мониторинг с Telegram-алертами и браузер PostgreSQL.

7разделов в панели
54 KBосновной JS-бандл
3слоя кэша под контролем
< 5 минот симптома до причины
Проблема

«Работает —
значит
всё ок.»

Собственный Node.js-сервис — REST API для нескольких проектов: блог, карта авиамаршрутов, ещё пара штук. Внутри PostgreSQL, двухуровневый кэш (in-memory + дисковый), HTTP-кэш публичных ответов. Всё живёт в Docker на VPS.

Проблема была в том, что я ничего не знал: сколько ключей сейчас в кэше, какой hit rate у конкретного эндпоинта, растёт ли heap после деплоя, когда кэш начал промахиваться. Ответить можно было только через docker logs или написав одноразовый скрипт. О проблемах узнавал первым пользователь — а не я.

Я захотел другого: чтобы система сама сообщала, что происходит прямо сейчас — и позволяла с этим что-то сделать без перезапуска сервера.

Архитектура панели

7 разделов,
каждый решает
свою задачу.

Не один «экран статистики», а набор специализированных инструментов — у каждого свой вопрос и своя аудитория.

01

Дашборд

Сводный экран: четыре stat-карточки (запросы, ошибки, кэш, аптайм), heap-прогресс, event loop p50/p95, пул соединений, API-метрики за час. Отвечает на вопрос «всё ли в порядке» с одного взгляда — без выбора метрик и диапазонов.

RPSerror ratep95 latencyheap
02

Кэш

Самый насыщенный раздел. Donut-чарты L1/HTTP, gauge heap-памяти, динамика RPS и hit rate за сессию. Статистика по эндпоинтам с нормализацией URL. Очистка — раздельно L1, L2, HTTP или всего. Браузер ключей с TTL, хитами и фильтрацией.

NodeCachehit/missEChartswildcard clear
03–05

Логи · Аналитика · Ошибки

Кольцевой буфер 5 000 запросов с персистентностью. Аналитика за выбранный период: latency-линия, топ медленных эндпоинтов, нормализованная таблица с p50/p95. Раздел ошибок с дельтой к предыдущему периоду — не «было 42», а «+61% к прошлому часу».

ndjsonrolling windowp95группировка 4xx/5xx
06

Мониторинг

Три подраздела: Telegram-боты (с тестом отправки прямо из UI), правила алертов (Error Rate / Status Codes / Slow Response — каждое с cooldown, тихими часами и своим ботом) и синтетические проверки — активный пинг по расписанию с настраиваемым порогом провалов до алерта.

Telegramcooldownsynthetic checkssilent hours
07

API-эксплорер

Живой каталог всех эндпоинтов: Query/Path/Body-параметры с типами и обязательностью, поля для значений, которые сразу дописываются в URL, и кнопка «Execute» — статус, время, размер и JSON-ответ с подсветкой синтаксиса. Список сервисов и маршрутов хранится в PostgreSQL, а не в статичном файле.

live catalogQuery/Path/BodyExecute
Модуль «API Сервисы»

Документация,
тест и каталог
в одном экране.

Каждый сервис — с документацией параметров и тестированием запросов прямо в панели: без Postman, без устаревших коллекций и без блуждания по исходникам в поисках названия параметра.

  1. 01
    Документация без отрыва от теста

    Под каждым методом — до трёх таблиц: Query, Path и Body. Имя, тип, default-значение, бейдж «required» или «optional» и описание. Если параметров нет — курсивная подсказка «Параметры не требуются», а не пустая таблица.

    Query/Path/Bodyrequired/optional
  2. 02
    Поле значения прямо в таблице

    У query-параметров есть пятая колонка — поле ввода. Вписал значение — оно тут же встало в строку запроса ниже, без ручного редактирования URL. Крестик рядом убирает параметр и из таблицы, и из URL одним кликом.

    inline inputавто-синхронизация URL
  3. 03
    Execute с полной картиной ответа

    Код статуса, время выполнения, размер ответа и количество элементов в массиве — всё в шапке блока ответа. JSON выводится тем же редактором кода, что и в файловом менеджере: подсветка ключей, строк, чисел и null без отдельной библиотеки.

    status · time · sizeпереиспользование code-editor
  4. 04
    Каталог в БД, а не в коде

    Список сервисов и методов хранится в двух таблицах PostgreSQL со связью по внешнему ключу и каскадным удалением — убрали сервис, его маршруты ушли сами. Новый эндпоинт добавляется через форму, без правки кода и пересборки фронтенда.

    PostgreSQLFK cascadeбез пересборки
  5. 05
    Параметры без миграций под каждый случай

    Документация query/path/body хранится в одном JSON-поле на маршрут, а не в таблице с фиксированной схемой. Форма редактора разворачивает его в три таблицы с типизированными значениями, страница тестирования — обратно в реальный запрос.

    JSON-полебез новых таблиц
Под капотом

Технические
решения.

Самые нетривиальные части — не «подключил библиотеку», а «почему именно так».

  1. 01
    ECharts с tree-shaking и manualChunks

    Из коробки ECharts весит ~800 KB — многовато для одной страницы. Импортирую только нужные компоненты через echarts/core, а в Vite добавляю manualChunks: { echarts: ['echarts', 'vue-echarts'] }. Итог: основной JS приложения — 54 KB, ECharts — 627 KB (217 KB gzip) отдельным чанком, который загружается один раз и кешируется браузером. На остальных страницах ECharts не загружается вообще.

    tree-shakingcode splitting54 KB main
  2. 02
    Hit rate: скользящее окно вместо накопительного

    Накопительный hit rate (общие хиты / все запросы с момента запуска) быстро теряет информативность — слишком инертен. Сбросили кэш или пошли новые запросы? Накопительный почти не отреагирует. Беру last1m.hitRate — процент за последнюю минуту из rolling-буфера. На линейном графике добавил visualMap: линия плавно меняет цвет от красного к зелёному в зависимости от значения по Y — проблема видна немедленно, не нужно смотреть на цифры.

    rolling windowECharts visualMapreactive color
  3. 03
    Алерты: три типа условий, per-rule cooldown, тихие часы

    Один глобальный порог error rate не работает в реальности: у /api/search нормальный error rate 8% (пользователи ищут несуществующее), у /api/payments уже 1% — проблема. Сделал независимые правила с фильтрами по URL и методу. Cooldown на каждом правиле отдельно — без него один инцидент генерирует 60 одинаковых сообщений в час. Тихие часы поддерживают переход через полночь. Всё хранится в PostgreSQL и читается при каждом тике — новое правило начинает работать через минуту без перезапуска.

    per-rule cooldownURL filtersilent hours
  4. 04
    Синтетические проверки: «провалов до алерта»

    Правила алертов — реактивные: смотрят на трафик, который уже пришёл. Если внешний сервис лёг ночью, а пользователей не было — алертов не будет. Синтетические проверки активно пингуют URL по расписанию через один setInterval на 60 секунд. Параметр «провалов до алерта» убирает ложные срабатывания от эфемерных сетевых ошибок: алерт приходит только если N проверок подряд провалились. Один успех — счётчик сбрасывается. Опциональная проверка строки в теле ловит случаи, когда сервис возвращает 200 с телом вида «status: maintenance».

    active pingingN failures thresholdbody match
  5. 05
    ctid для редактирования строк без первичного ключа

    Удаление и обновление строк в браузере БД работают через системный столбец PostgreSQL ctid — физический адрес строки. Бэкенд добавляет ctid::text as __ctid к каждому SELECT и использует в WHERE ctid = ?::tid. Это работает для любой таблицы независимо от наличия первичного ключа — включая view и legacy-таблицы без id.

    ctidPostgreSQL internalsбез PK
В деле

5 минут
от симптома
до причины.

Открываю панель утром — дашборд показывает error rate 4.2%, жёлтый. RPS в норме, деплоев с вечера не было. Иду в «Ошибки»: дельта +61% к предыдущему часу, всё в 5xx. Тайм-лайн — всплеск с 7:40.

В таблице группировки одна доминирующая строка: GET /api/airlines/* 503, 38 срабатываний за час. Иду в «Аналитику», окно 6 часов, фильтр /api/airlines, сортировка по p95 — у /api/airlines/graphql p95 1840 мс, у остальных в пределах 200 мс. Проблема локализована.

«Логи», фильтр: GET, 5xx, /api/airlines — все ошибки с одинаковым телом: таймаут базы данных. Дашборд: пул соединений — 10 всего, 0 idle, пул исчерпан. «API-эксплорер», запрос статистики БД прямо из браузера — один запрос висит уже 12 минут. Дальше — уже дело к коду.

Раньше тот же путь: docker logs | grep 503 плюс ручные запросы через curl. Занимало не 5 минут, а гораздо больше — и то если повезёт с логами.

Скриншоты

Как это
выглядит.

«Хорошая наблюдаемость — это не когда ты смотришь на мониторинг, а когда мониторинг смотрит за системой вместо тебя и приходит только когда нужен.»

— из опыта разработки Macrulez App Container
Следующий шаг

Нужна наблюдаемость или админ инструменты?

Спроектирую и реализую от дашборда до браузера данных. Работаю с Node.js, Vue 3 и PostgreSQL.