Files
slm-design/.opencode/skills/rest-client/reference/canons/http-client.md
2026-08-01 09:31:08 +03:00

7.2 KiB
Raw Blame History

title, description, keywords
title description keywords
Кастомизация HTTP-клиента Настройка транспорта REST-клиента через опции и хуки HttpClient.
rest
http
транспорт
авторизация
jwt
refresh token
onRequest
onError

Кастомизация HTTP-клиента

Настройка транспорта REST-клиента через опции и хуки HttpClient.

Где живёт кастомизация

Вся настройка транспорта — baseUrl, заголовки, авторизация, retry — задаётся в client.ts REST-модуля при создании HttpClient.

Не размещайте авторизацию, обработку 401 и логирование в компонентах, GET-хуках или обёртках над операциями: у транспорта одна точка настройки.

В generated/SDK-сценарии HttpClient импортируется из generated-кода или SDK-пакета. В ручном сценарии без OpenAPI HttpClient импортируется из runtime-зависимости @gromlab/api-codegen.

import { HttpClient } from '@gromlab/api-codegen'

Опции HttpClient

HttpClient принимает плоский конфиг: стандартные fetch-опции задаются вместе с хуками клиента.

Опция Назначение
baseUrl Базовый URL API.
headers Заголовки по умолчанию для всех запросов.
credentials Политика отправки cookies: omit, same-origin, include.
timeout Таймаут запроса в миллисекундах, работает через AbortSignal.
customFetch Замена стандартного fetch: тесты, SSR, custom transport.
paramsSerializer Кастомная сериализация query params в URL.
responseParser Кастомный парсинг response body.
onRequest Request-хук перед вызовом fetch.
onResponse Response-хук после успешного HTTP-ответа.
onError Error-хук для HTTP-ошибок, network errors и ошибок парсинга.

Полный список опций — в README @gromlab/api-codegen.

Контракт хуков

  • onRequest(params, context) вызывается перед fetch и возвращает изменённые params.
  • onResponse(response, context) вызывается после успешного ответа и возвращает response.
  • onError(error, context) вызывается для HTTP-ошибок, network errors и ошибок парсинга.
  • context содержит url, request, retryCount и retry() — повтор текущего запроса.
  • onError должен либо бросить ошибку, либо вернуть fallback-значение, либо вернуть результат context.retry(). Если вернуть undefined, ошибка будет считаться обработанной, а вызывающий код получит undefined вместо исключения.
  • Для защищённых endpoints generated operation передаёт secure: true, поэтому авторизацию можно добавлять только там, где она нужна.

JWT-авторизация

Токен добавляется в onRequest только для защищённых запросов и не перезаписывает явно переданный Authorization.

// src/infra/pet-store-rest-api/client.ts
export const petStoreHttpClient = new HttpClient({
  baseUrl: 'https://example.com/api',
  onRequest: (params) => {
    const token = localStorage.getItem('access_token')

    if (!params.secure || !token) {
      return params
    }

    const headers = new Headers(params.headers)

    if (!headers.has('Authorization')) {
      headers.set('Authorization', `Bearer ${token}`)
    }

    return {
      ...params,
      headers,
    }
  },
})

Refresh token

Обновление токена и повтор запроса выполняются в onError через context.retry(). context.retryCount защищает от бесконечного цикла повторов.

// src/infra/pet-store-rest-api/client.ts
import { ApiError, HttpClient } from './generated'

export const petStoreHttpClient = new HttpClient({
  baseUrl: 'https://example.com/api',
  onError: async (error, context) => {
    if (error instanceof ApiError && error.status === 401 && context.retryCount === 0) {
      await refreshToken()
      return context.retry()
    }

    throw error
  },
})

ApiError экспортируется из generated/ и содержит status, statusText, response, data и исходный request.

Логирование

const petStoreHttpClient = new HttpClient({
  baseUrl: 'https://example.com/api',
  onResponse: (response, context) => {
    console.log(context.request.method, context.url, response.status)
    return response
  },
})

Сериализация query params

const petStoreHttpClient = new HttpClient({
  baseUrl: 'https://example.com/api',
  paramsSerializer: (query) => {
    const params = new URLSearchParams()

    Object.entries(query).forEach(([key, value]) => {
      if (Array.isArray(value)) {
        params.set(key, value.join(','))
        return
      }

      if (value !== undefined) {
        params.set(key, String(value))
      }
    })

    return params.toString()
  },
})

Параметры одного вызова

Разовые настройки запроса не относятся к HttpClient. Они передаются последним аргументом operation:

import { getPetById } from './generated/operations/get-pet-by-id'
import { petStoreHttpClient } from './client'

await getPetById(
  petStoreHttpClient,
  { petId },
  {
    headers: {
      'X-Request-Id': requestId,
    },
  },
)

Правила

  • Кастомизация транспорта живёт только в client.ts REST-модуля.
  • Авторизация добавляется в onRequest с учётом params.secure и без перезаписи явного Authorization.
  • onError либо бросает ошибку, либо возвращает fallback или context.retry(); молчаливый return запрещён.
  • Повторы запроса ограничиваются проверкой context.retryCount.
  • Бизнес-реакции на ошибки — тосты, редиректы, UI-состояние — не размещаются в хуках HttpClient.

Следующий шаг

После настройки транспорта проверьте использование REST-клиента или добавьте GET-хуки REST-клиента.