Перейти до вмісту

Каскадна маршрутизація

Каскадна маршрутизація дозволяє визначити кілька upstream цілей для одного запиту. MirApi спробує їх послідовно (Priority) або паралельно (Race) і поверне першу успішну відповідь.

Каскадні маршрути налаштовуються у вашому дашборді та активуються заголовком X-Route-Key.

Налаштування маршруту у дашборді

Section titled “Налаштування маршруту у дашборді”
  1. Відкрийте Routes → Створити маршрут
  2. Вкажіть ім’я (наприклад, ai-providers)
  3. Додайте цілі — список повних URL через кому:
    https://api.openai.com/v1/chat/completions,
    https://api.anthropic.com/v1/messages,
    https://api.groq.com/openai/v1/chat/completions
  4. Оберіть стратегію: Priority або Race
  5. Задайте тайм-аут на ціль (мілісекунди)
  6. Опційно налаштуйте body mapping та витягнення відповіді

Дашборд списку каскадних маршрутів

Terminal window
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Route-Key: ai-providers" \
-H "X-Identity-Key: Bearer $OPENAI_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Привіт"}]}'

Priority (Послідовний failover)

Section titled “Priority (Послідовний failover)”

Цілі пробуються по порядку. Наступна ціль пробується лише якщо попередня провалилась (5xx, тайм-аут або помилка з’єднання).

Ціль 1: https://api.openai.com/... → збій (503)
Ціль 2: https://api.anthropic.com/... → збій (тайм-аут)
Ціль 3: https://api.groq.com/... → успіх ✓
Відповідь: X-Rescued: cascade_fallback

Найкраще для:

  • Економії коштів (лише один API-виклик у happy path)
  • Платіжних API де потрібний детермінований порядок
  • Будь-яких сценаріїв де важливий пріоритет
Terminal window
# AI провайдер — спочатку OpenAI, потім Anthropic, потім Groq
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Route-Key: ai-priority" \
-H "X-Identity-Key: Bearer $OPENAI_KEY" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Що таке Go?"}]}'

Всі цілі викликаються одночасно. Перша успішна відповідь перемагає; решта скасовуються через context cancellation.

Ціль 1: https://api.openai.com/... → відповів за 1200ms ✗ (програв)
Ціль 2: https://api.anthropic.com/... → відповів за 890ms ✗ (програв)
Ціль 3: https://api.groq.com/... → відповів за 89ms ✓ (переміг)

Найкраще для:

  • Ультранизьких затримок де час відповіді критичний
  • Read-only запитів (курси валют, пошук, ембеддинги)
  • Коли всі провайдери повертають еквівалентні відповіді
Terminal window
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Route-Key: ai-race" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Швидке питання"}]}'

Комбінування з іншими функціями

Section titled “Комбінування з іншими функціями”
Terminal window
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Route-Key: payment-cascade" \
-H "X-Retry-Count: 2" \
-H "X-Retry-Delay: 300ms" \
-H "X-Proxy-Timeout: 10s" \
-d '{"amount": 9900, "currency": "usd"}'
# Ціль 1 повторюється до 2 разів перед переходом до Цілі 2
Terminal window
curl -X POST https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Route-Key: payment-cascade" \
-H "X-Webhook-Callback: https://your-app.com/payments/done" \
-H "X-Proxy-Idempotency-Key: order_789_pay_1" \
-d '{"amount": 9900, "currency": "usd"}'
# Повертає 202 негайно
# Воркер виконує повний каскад у фоні
# POST з першою успішною відповіддю на callback
Terminal window
curl https://proxy.mirapi.io/ \
-H "X-MirApi-Key: $MIRAPI_KEY" \
-H "X-Route-Key: exchange-rates-cascade" \
-H "X-Smart-Cache: 300s"
# Пробує всі цілі маршруту
# Всі провалились → кеш → X-Rescued: cache

Маппінг тіла запиту цілі

Section titled “Маппінг тіла запиту цілі”

Маппінг тіла запиту розбирає JSON-тіло вхідного запиту, перейменовує ключі, обходить вкладені поля та масиви, і формує запит перед відправкою на upstream. Перейменовані ключі-джерела автоматично видаляються.

Формат правила маппінгу — розділений комами список правил: шлях_джерела=>шлях_цілі або шлях_джерела=>шлях_цілі(шаблон).

Підтримуваний синтаксис шляху

Section titled “Підтримуваний синтаксис шляху”
МожливістьПрикладОпис
Плоске перейменуванняamount=>sumПерейменовує кореневий ключ
Вкладений об’єкт (dot-notation)data.order=>data.order_idПерейменовує order на order_id всередині об’єкта data
Перебір масиву з []data.orders[].order=>data.orders[].order_idПерейменовує order на order_id у кожному елементі масиву orders
Перебір масиву без []data.orders.order=>data.orders.order_idТе саме — дужки необов’язкові, визначаються автоматично
Шаблон значення {value}order=>order_text(Order ID: #{value})Форматує значення поля у рядковий шаблон
Міжпольовий шаблон {field_name}status=>status(Order {order} status is {value})Інтерполює значення інших полів з поточного JSON-контексту
  1. Ваш клієнт надсилає запит до проксі-шлюзу з оригінальною структурою JSON.
  2. Шлюз перехоплює тіло запиту і перед відправкою на upstream перейменовує та трансформує ключі згідно з body_map цієї цілі.
  3. Вкладені шляхи та елементи масивів обходяться автоматично. Решта ключів залишаються без змін.

A. Плоске перейменування:

// body_map: "amount=>sum, currency=>cur"
// Вхід: {"amount": 100, "currency": "USD"}
// Вихід: {"sum": 100, "cur": "USD"}

B. Вкладені об’єкти:

// body_map: "data.order=>data.order_id"
// Вхід: {"action": "get_status", "data": {"order": 353454876}}
// Вихід: {"action": "get_status", "data": {"order_id": 353454876}}

C. Перебір масиву:

// body_map: "data.orders[].order=>data.orders[].order_id"
// або "data.orders.order=>data.orders.order_id"
// Вхід: {"data": {"orders": [{"order": 101}, {"order": 102}]}}
// Вихід: {"data": {"orders": [{"order_id": 101}, {"order_id": 102}]}}

D. Динамічний шаблон значення {value}:

Форматує значення поля у рядковий шаблон. {value} — пластина для значення джерельного поля:

// body_map: "order=>order_text(Order ID: #{value})"
// Вхід: {"order": 101}
// Вихід: {"order_text": "Order ID: #101"}

E. Міжпольовий шаблон {field_name}:

Інтерполює значення інших полів з того ж JSON-контексту. Поле шукається спочатку в поточному об’єкті, а потім у глобальному корені:

// body_map: "status=>status(Order {order} status is {value})"
// Вхід: {"order": 101, "status": "success"}
// Вихід: {"order": 101, "status": "Order 101 status is success"}

F. Посилання на сусідні поля у масивах:

Доступає до сусідніх ключів всередині кожного окремого елементу масиву під час ітерації:

// body_map: "data.orders.status=>data.orders.status_text(Order {order} is {value})"
// Вхід: {"data": {"orders": [{"order": 101, "status": "success"}, {"order": 102, "status": "fail"}]}}
// Вихід: {"data": {"orders": [{"order": 101, "status_text": "Order 101 is success"}, {"order": 102, "status_text": "Order 102 is fail"}]}}
---
## Власні умови відкату по тілу відповіді
За замовчуванням каскадна маршрутизація переходить до наступної цілі лише тоді, коли поточний ендпоінт повертає статус `5xx`, виникає тайм-аут або помилка з'єднання.
Однак багато API (особливо платіжні шлюзи) повертають статус `200 OK` навіть тоді, коли транзакцію відхилено або вона завершилася помилкою. Для підтримки маршрутизації на основі бізнес-логіки збоїв, кожна ціль маршруту може визначати власну умову відкату у дашборді чи базі даних за допомогою двох параметрів:
- `fallback_field`: вираз JSONPath, що вказує на поле в JSON відповіді для перевірки. Префікс `$.` є **необов'язковим** — додається автоматично, якщо відсутній. Підтримуються вкладені об'єкти (dot-notation) та елементи масиву (нотація `[]`).
- `fallback_value`: рядкове значення для співпадіння (регістронезалежна перевірка на входження підрядка).
### Підтримуваний синтаксис `fallback_field`
| Приклад | Еквівалентно | Опис |
|---|---|---|
| `status` | `$.status` | Поле на верхньому рівні |
| `error.code` | `$.error.code` | Поле вкладеного об'єкта |
| `errors[].code` | `$.errors[*].code` | Поле всередині будь-якого елемента масиву |
### Як це працює:
1. Цільовий ендпоінт повертає успішний код статусу (менше `500`).
2. Шлюз зчитує тіло відповіді та витягує значення поля за вказаним шляхом JSONPath у `fallback_field`.
3. Якщо витягнуте значення містить `fallback_value` як підрядок, шлюз розглядає цю відповідь як **помилку**.
4. Шлюз відкидає цю відповідь, логує спрацювання умови та негайно переходить до наступної цілі у ланцюжку каскаду.
![Дашборд налаштування умов відкату](../../assets/cascade_1.png)
### Приклад: Відкат при відхиленні платежу
Upstream платіжного процесора повернув статус `200 OK`, але платіж відхилено:
```json
{
"transaction_id": "tx_abc123",
"status": "declined",
"failure_reason": "insufficient_funds"
}

Налаштувавши ціль параметрами:

  • Fallback Field: status (або $.status)
  • Fallback Value: declined

Шлюз перехопить 200 OK, виявить, що $.status дорівнює "declined", трактуватиме це як збій і надішле запит на оплату до наступного резервного банку у вашому каскаді.

Приклад: Відкат за кодом помилки в масиві

Section titled “Приклад: Відкат за кодом помилки в масиві”

Upstream повертає помилки як масив:

{
"errors": [{"code": "card_declined", "message": "Недостатньо коштів"}]
}

Налаштувавши ціль параметрами:

  • Fallback Field: errors[].code
  • Fallback Value: card_declined

Шлюз перевіряє поле code кожного елемента масиву errors і тригерить відкат, якщо будь-яке з них співпадає.


Шлюз валідує всі цільові URL щодо власних IP-адрес та хостнеймів. Будь-яка цільова URL що резолвиться до самого проксі блокується з 400 Bad Request.

Коли використовується не первинна ціль:

HTTP/1.1 200 OK
X-Rescued: cascade_fallback

Якщо первинна ціль (перша у списку) успішна — X-Rescued не додається.