Стандартизация успешных ответов и ошибок в Metabot
Общий подход к обработке результатов между модулями, API и бизнес-логикой
В Metabot разные части системы постоянно обмениваются результатами выполнения операций:
- JS-плагины вызывают другие плагины;
- low-code сценарии запускают кастомный код;
- API-эндпоинты принимают запросы извне;
- внутренние модули возвращают данные друг другу.
Если каждый компонент будет возвращать ответы в своём формате, система очень быстро превращается в хаос:
- где-то
true / false, - где-то
throw error, - где-то
{ status: "ok" }, - где-то просто строка.
Чтобы этого избежать, в Metabot используется единый стандарт ответов — плагин
Common.Utils.Response.
Зачем вообще стандартизировать ответы
Стандартизация решает сразу несколько задач:
- 📦 единый формат для успешных результатов и ошибок;
- 🔗 предсказуемое взаимодействие между модулями;
- 🧠 упрощение логики: не нужно каждый раз гадать, что вернёт функция;
- 🌐 удобный проброс результатов во внешние API;
- 🧪 возможность централизованно логировать и анализировать ошибки.
Ключевая идея простая:
Любая функция либо возвращает success-response, либо error-response. Исключения — редкость, а не норма.
Где используется Common.Utils.Response
Плагин применяется практически везде:
- в JS-плагинах (
Common.Helpers.*,Common.CJM.*, AI-модули); - во внутренних API-эндпоинтах;
- в бизнес-логике;
- в интеграциях с внешними системами;
- в helper-функциях, которые могут вызываться цепочкой.
Если вы пишете свой модуль — используйте этот подход, либо создайте свой аналог по той же схеме.
Базовая идея формата ответа
Ответ всегда — это объект.
Успешный результат
{
success: true,
...data
}
Ошибка
{
success: false,
error: true,
message: "Описание ошибки",
error_code: "OPTIONAL_CODE"
}
Это минимальный контракт, который:
- легко проверять (
if (!result.success)); - удобно сериализовать;
- безопасно передавать между слоями системы.
Подключение плагина Common.Utils.Response
Перед использованием стандартных функций для возврата успешных ответов и ошибок плагин необходимо подключить в вашем JS-модуле.
Пример подключения:
// Подключаем Response плагин для обработки ответов
const { getErrorResponse, getSuccessResponse } = require('Common.Utils.Response');
После подключения функции getSuccessResponse и getErrorResponse доступны для использования в:
- helper-плагинах;
- бизнес-логике;
- API-эндпоинтах;
- кастомных скриптах внутри Metabot.
📌 Плагин Common.Utils.Response является общим и доступен на всех серверах Metabot, поэтому дополнительной установки не требуется.
Основные функции плагина
Плагин Common.Utils.Response предоставляет набор helper-функций.
getSuccessResponse()
Используется при успешном выполнении операции.
Примеры использования:
return getSuccessResponse("Операция выполнена успешно")
или
return getSuccessResponse({
message: "Скрипт создан",
script_id: 12345
})
getErrorResponse()
Используется для возврата ошибки без выбрасывания исключения.
return getErrorResponse("Не удалось отправить сообщение")
или
return getErrorResponse(
{ message: "Ошибка валидации", field: "email" },
"VALIDATION_ERROR"
)
Почему это важно: ошибка становится данными, а не неконтролируемым исключением.
Дополнительные типы ответов
Плагин также содержит готовые шаблоны для типовых ситуаций:
-
getValidationErrorResponse()— ошибки валидации; -
getNotFoundResponse()— сущность не найдена; -
getForbiddenResponse()— доступ запрещён.
Их удобно использовать в API и сервисных методах, чтобы не изобретать форматы заново.
Почему мы не кидаем exceptions везде
В классическом backend-подходе ошибки часто бросаются через throw.
В Metabot это не основной сценарий, по нескольким причинам:
-
JS-код выполняется внутри движка (V8), а не в отдельном Node-процессе.
-
Исключения могут:
- прервать выполнение сценария;
- привести к неочевидным последствиям;
- быть плохо читаемыми в логах.
-
Большинство ошибок — ожидаемые:
- не найден скрипт,
- пустой параметр,
- недоступен внешний сервис,
- неверный формат данных.
Поэтому:
- ошибки возвращаются как результат;
-
throwиспользуется только для действительно критических ситуаций.
Как это работает в цепочках вызовов
Типичный сценарий:
const result = sendFormattedMessage(text, 'HTML')
if (!result.success) {
// можно залогировать
// можно показать fallback
// можно вернуть ошибку выше
return result
}
// продолжаем логику
Таким образом:
- каждый уровень решает, что делать с ошибкой;
- ошибка может «подняться» наверх без искажения;
- формат ответа остаётся единым на всём пути.
Использование в API-эндпоинтах
Внутренние и внешние API Metabot обычно выглядят так:
try {
// бизнес-логика
return getSuccessResponse({ result })
} catch (e) {
return getErrorResponse(e.message)
}
Снаружи клиент всегда получает предсказуемый JSON, независимо от источника ошибки.
Это критично для:
- фронтендов;
- интеграций;
- автоматических сценариев;
- AI-агентов, которые анализируют ответы.
Исходный код плагина Common.Utils.Response
/**
* 📦 Плагин: Common.Response.Wrapper
* Автор: @ArtemGarashko
* Версия: 1.4
* Дата последнего обновления: 26 апр 2025
*
* Назначение:
* - Формирование ответов об успешных и неудачных операциях.
* - Используется в API, между компонентами, в бизнес-логике.
*/
function getErrorResponse(errorOrData, errorCode = null) {
const result = { success: false, error: true }
if (typeof errorOrData === "string") {
result.message = errorOrData
} else if (typeof errorOrData === "object") {
Object.assign(result, errorOrData)
}
if (errorCode) {
result.error_code = errorCode
}
return result
}
function getSuccessResponse(dataOrMessage = {}) {
const result = { success: true }
if (typeof dataOrMessage === "string") {
result.message = dataOrMessage
} else if (typeof dataOrMessage === "object") {
Object.assign(result, dataOrMessage)
}
return result
}
function getValidationErrorResponse(details) {
return {
success: false,
error: true,
message: details.message || "Validation failed",
fields: details.fields || []
}
}
function getNotFoundResponse(entity = "Entity") {
return {
success: false,
error: true,
message: `${entity} not found`
}
}
function getForbiddenResponse(reason = "Access denied") {
return {
success: false,
error: true,
message: reason
}
}
exports.getErrorResponse = getErrorResponse
exports.getSuccessResponse = getSuccessResponse
exports.getValidationErrorResponse = getValidationErrorResponse
exports.getNotFoundResponse = getNotFoundResponse
exports.getForbiddenResponse = getForbiddenResponse
⚠️ Версия
Common.Utils.Response, приведённая в статье, может отличаться от версии, установленной на сервере Metabot. Используйте код как ориентир и архитектурный шаблон.
Связь с предыдущим уроком
В уроке про форматированные сообщения мы использовали этот подход:
-
плагин
sendFormattedMessageотправляет сообщение; -
но возвращает стандартизированный результат;
-
вызывающая сторона может:
- проигнорировать результат;
- обработать ошибку;
- использовать данные дальше.
То есть:
helper-плагины делают действие response-плагин описывает результат
Это разделение ответственности — важный архитектурный принцип.
Можно ли сделать свой стандарт
Да, конечно.
Common.Utils.Response — это:
- рекомендованный стандарт;
- доступный на всех серверах Metabot;
- проверенный в бою.
Но если вам нужен:
- другой формат;
- дополнительные поля;
- специфичная структура под внешний API —
вы можете:
- скопировать подход;
- реализовать свой response-helper;
- использовать его внутри своего продукта.
Главное — консистентность.
Итог
В этом уроке мы зафиксировали ключевую практику Metabot:
- ошибки — это данные, а не исключения;
- успешные результаты и ошибки имеют единый формат;
- плагины и модули легко комбинируются;
- API и внутренняя логика говорят на одном языке.
Если при проектировании нового модуля у тебя возникает мысль «а может тут просто вернуть true?» — это хороший момент остановиться и проверить: а не ломаешь ли ты этим общую архитектурную договорённость.
Именно такие мелкие решения потом определяют, будет система масштабируемой или нет.
Нет комментариев