﻿# OpenRouter в Node.js: серверный вызов без ключа в браузере

Как вызвать OpenRouter из серверного Node.js через fetch: ключ в окружении, модель, проверка ответа, ограничение времени и безопасная граница между браузером и API.

Автор: Команда Retrivium
Обновлено: 2026-11-01
Канонический URL: https://retrivium.ai/ru/journal/openrouter-nodejs-request/
Language: ru

Для первого обращения к OpenRouter из Node.js достаточно серверного `fetch`. Главная архитектурная граница проходит между браузером пользователя и процессом, которому разрешено тратить средства API. Ключ OpenRouter должен оставаться у второго, даже если интерфейс сайта написан на JavaScript.

Ниже приведён небольшой пример Chat Completions. Он предполагает версию Node.js со встроенными `fetch` и `AbortSignal.timeout`; конкретную среду выполнения проверьте перед переносом.

## Отделите серверный вызов

В [Quickstart OpenRouter](https://openrouter.ai/docs/quickstart) описан единый доступ к моделям, а [руководство аутентификации](https://openrouter.ai/docs/api_reference/authentication) показывает Bearer-заголовок. Не помещайте этот заголовок в код, который сборщик отправляет браузеру.

```javascript
const key = process.env.OPENROUTER_API_KEY;
const model = process.env.OPENROUTER_MODEL;
if (!key || !model) throw new Error('Missing server configuration');

const response = await fetch(
  'https://openrouter.ai/api/v1/chat/completions',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${key}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model,
      messages: [{ role: 'user', content: 'Ответь: готово' }],
      max_tokens: 32,
    }),
    signal: AbortSignal.timeout(60000),
  },
);
const data = await response.json();
if (!response.ok || data.error) {
  throw new Error(`API request failed: ${response.status}`);
}
console.log(data.id, data.model);
```

Модель выбирается через окружение, а не фиксируется случайным устаревающим названием. Этот тест может расходовать средства по условиям выбранного маршрута.

## Ограничьте возможности входящего клиента

В рабочем приложении браузер обращается к вашему внутреннему маршруту. Сервер решает, какие параметры передать дальше. Если позволить посетителю задавать любую модель, размер ответа и число параллельных вызовов, серверный секрет сам по себе не ограничит расходы.

Для начала примите небольшой текст и фиксированный набор параметров. Добавьте ограничения частоты и размера данных, соответствующие вашему сценарию. Эти меры должны проверяться отдельными тестовыми запросами, а не только присутствовать в описании проекта.

## Разделяйте состояния ошибки

Сбой DNS, тайм-аут, неуспешный HTTP-статус и ошибка внутри JSON имеют разные причины. В примере вывод пользователю намеренно сокращён; во внутреннем отчёте можно сохранить безопасные поля ошибки и идентификатор операции, исключив секреты и ненужное содержимое запроса.

Не добавляйте бесконечный повтор в `catch`. Если фронтенд, ваш backend и внешний gateway одновременно повторяют одну операцию, фактическое число вызовов может стать неожиданным. Сначала назначьте один слой, отвечающий за повторы, и общий предел времени.

## Проверьте поведение при отмене

Пользователь может закрыть вкладку раньше завершения ответа. Определите, прекращает ли это работу вашего backend и что происходит с внешним запросом. Прерывание локального ожидания не является универсальным подтверждением прекращения начислений у любого провайдера.

Условный пилот должен включать обычный ответ, отказ по лимиту, медленный ответ и разрыв клиентского соединения. Результаты показывают поведение вашей интеграции. Они не служат сравнительным бенчмарком моделей или облаков.

Отдельно ограничьте размер принимаемого ответа и правила его хранения. Успешный короткий тест не проверяет поведение на длинном содержимом. Если результат будет использоваться как JSON или аргументы инструмента, потребуется соответствующая проверка структуры до передачи следующему компоненту приложения.

## Сохраните границы ответственности

Зафиксируйте, где хранятся настройки, кто видит журналы и кто меняет бюджет. Для следующего этапа прочитайте про [ключ приложения](/ru/journal/openrouter-api-key-setup/) и [streaming](/ru/journal/openrouter-streaming-client/).
