# Маршруты на JavaScript (/ru/docs/mocks/scripted-routes)

[Маршруты, созданные вручную](/docs/mocks/routes), и спецификация отвечают заранее записанными ответами. Иногда моку
нужно больше: запомнить заказ, который только что сделал клиент, вычислить ответ или упасть на третьем вызове.
**Маршруты на JavaScript** — это небольшие обработчики, которые отвечают на запросы к моку первыми. Их переменные
живут, пока мок работает, поэтому между запросами они могут хранить состояние. Всё, что маршрут не обработал, идёт
дальше: к маршрутам мока, созданным вручную, а затем к его спецификации.

## Написание маршрутов [#writing-routes]

Откройте мок на странице «Моки» и прокрутите до карточки **Маршруты на JavaScript**. Напишите маршруты в редакторе и
нажмите **Сохранить маршруты**. **Вернуть** возвращает то, что было сохранено.

<Shot id="mocks-routes" alt="Карточка «Маршруты на JavaScript» мока с кодом, который хранит заказы в списке, строка Running: POST /orders, GET /orders/:id под редактором и журнал, где у каждого запроса к заказам указан ответивший маршрут скрипта" />

В этом примере, для мока спецификации Alpenglow, хранятся заказы, которые делает клиент, и возвращаются по ID:

```js
let orders = []

mock.post("/orders", (req, res) => {
  const order = { id: "ord_" + (orders.length + 1), status: "pending", ...req.json() }
  orders.push(order)
  res.status(201).header("Location", "/v1/orders/" + order.id).json(order)
})

mock.get("/orders/:id", (req, res) => {
  const order = orders.find(o => o.id === req.params.id)
  return order ?? res.status(404).json({ error: "no such order" })
})
```

После сохранения карточка перечисляет маршруты, которые зарегистрировал скрипт
(`Running: POST /orders, GET /orders/:id`), или оранжевым — почему скрипт не загрузился. Маршруты хранятся в
`mocks/<name>.js` рядом с настройками мока, так что вместе с рабочим пространством попадают в Git.

## Когда выполняются маршруты [#when-routes-run]

- Маршруты на JavaScript отвечают **первыми**, до маршрутов, созданных вручную, и спецификации. Отвечает первый
  маршрут, у которого совпали метод и путь.
- Пути отсчитываются **от пути сервера спецификации**, если мок обслуживает спецификацию: пишите `/orders`, а не
  `/v1/orders`.
- Маршрут, который ничего не задаёт и ничего не возвращает, **передаёт запрос дальше** — маршрутам, созданным вручную,
  и спецификации, которые отвечают как обычно. Так можно обрабатывать лишь некоторые запросы, например с определённым
  параметром запроса.
- Скрипт выполняется один раз, когда мок запускается. Его переменные верхнего уровня и объект `state` живут, пока
  мок не остановится.
- **Сохранение перезапускает маршруты с чистым состоянием.** Изменения спецификации, маршрутов, созданных вручную,
  или настроек мока его сохраняют.
- Запросы обрабатываются по одному, поэтому состояние остаётся согласованным.

[Журнал](/docs/mocks#the-log) мока помечает каждый запрос, обработанный маршрутом, именем маршрута: наведите на него
указатель, чтобы увидеть **Ответил этот маршрут** или **Передан дальше этим маршрутом** (маршрутам, созданным вручную,
или спецификации). То, что маршрут выводит через `console.log`, появляется в журнале под запросом.

## API маршрутов [#the-routes-api]

Маршруты регистрируются через `mock` и метод:

| Функция | Соответствует |
| --- | --- |
| `mock.get(path, handler)` | `GET` |
| `mock.post`, `mock.put`, `mock.patch`, `mock.delete`, `mock.head`, `mock.options` | Этому методу |
| `mock.any(path, handler)` | Любому методу |

В пути `:name` соответствует одному сегменту и помещает его в `req.params.name`; `*` соответствует остатку пути.

Обработчик получает `req` и `res`:

| `req` | Что содержит |
| --- | --- |
| `req.method` | Метод, заглавными буквами. |
| `req.path` | Путь, отсчитываемый от пути сервера спецификации. |
| `req.params` | Сегменты пути `:name`. |
| `req.query` | Параметры запроса, для каждого имени — последнее значение. |
| `req.headers` | Заголовки, с именами в нижнем регистре. |
| `req.body` | Тело как текст. |
| `req.json()` | Тело, разобранное как JSON, или `null`, если тела нет. |

| `res` | Что делает |
| --- | --- |
| `res.status(code)` | Задаёт статус. По умолчанию `200`. |
| `res.header(name, value)` | Добавляет заголовок. |
| `res.json(value)` | Отправляет значение как JSON с `Content-Type: application/json`, если вы не задали другой. |
| `res.send(text)` | Отправляет текст с `Content-Type: text/plain; charset=utf-8`, если вы не задали другой. |

Каждая функция `res` возвращает `res`, поэтому вызовы можно объединять в цепочку: `res.status(201).json(order)`.
Значение, которое возвращает обработчик, отправляется как JSON, так что `return order` — то же, что `res.json(order)`.
Обработчики могут быть `async`.

`state` (он же `mock.state`) — объект, который сохраняется между запросами, для того, что вам не хочется держать в
собственной переменной:

```js
mock.get("/me", (req, res) => {
  state.calls = (state.calls ?? 0) + 1
  if (state.calls % 3 === 0) return res.status(503).json({ error: "try again" })
  // Nothing set: the routes made by hand or the spec answer this request.
})
```

## Ограничения [#limits]

Маршруты выполняются в той же песочнице, что и [скрипты запросов](/docs/requests/scripts-and-tests), а не в Node: без
файловой системы и без сети. На каждый запрос JavaScript может работать 2 секунды, и ещё 2 секунды у скрипта есть на
запуск. Обработчик, который выбрасывает исключение, отвечает `500` с текстом ошибки, она видна и в журнале.

## Доверие к маршрутам [#trusting-routes]

Маршруты — это код, который выполняется на вашем Mac, поэтому для них то же правило, что и для скриптов запросов.
Маршруты, которые вы пишете и сохраняете в Echolot, выполняются. Маршруты, пришедшие извне — через Git, импорт или
другое приложение, которое правит файл, — нет: карточка сообщает **Эти скрипты пришли не из Echolot**, и мок не
запускается, пока вы не прочитаете их и не нажмёте **Доверять скриптам**. Если они снова изменятся вне Echolot,
Echolot спросит ещё раз.

Агенты могут читать маршруты мока, но никогда не пишут их.

## Из командной строки [#from-the-command-line]

`echolot mock -w <workspace> <mock>` запускает мок рабочего пространства со всеми его маршрутами; `--routes <file>`
запускает маршруты на JavaScript из другого файла, в том числе вместе с файлом спецификации. См.
[echolot mock](/docs/cli/mock).
