import {Meta} from '@storybook/addon-docs/blocks'

<Meta title='@dxtmisha/ru/functional-basic/Classes/ApiCache - Кэширование ответов API'/>

# Класс ApiCache

Класс для кэширования ответов API. Он позволяет сохранять результаты запросов в памяти или во внешнем хранилище, предотвращая избыточные сетевые вызовы в течение заданного периода. В текущей реализации кэширование по умолчанию активно только на стороне сервера (SSR) для ускорения рендеринга и снижения нагрузки на API при одновременных запросах.

> `ApiCache` является **вспомогательным классом**, предназначенным для низкоуровневого управления кэшем. В большинстве случаев рекомендуется взаимодействовать с кэшированием через параметр `cache` в `ApiInstance` или глобальный объект `Api`.

## Основные возможности

- **Автоматическое управление ключами** — генерирует уникальные ключи кэша на основе параметров запроса (путь, метод, тело запроса и т.д.).
- **Гибкий TTL (Time To Live)** — позволяет устанавливать время хранения для каждого запроса индивидуально или использовать значение по умолчанию (24 часа).
- **Оптимизация SSR** — предотвращает ненужные вызовы API в среде Node.js. По умолчанию отключено в браузере (работает только если явно включено через параметры конфигурации).
- **Поддержка слушателей** — возможность подключения внешнего хранилища (например, Redis, Nitro Storage) через функции обратного вызова.
- **Фоновая очистка** — автоматически удаляет устаревшие записи при обращении к кэшу.

## Методы

### Действие

Методы для управления записями кэша.

- `get<T>(key: string): Promise<T | undefined>` — Извлекает значение из кэша.
- `getByFetch<T>(fetch: ApiFetch): Promise<T | undefined>` — Извлекает значение, используя опции запроса как источник ключа.
- `set<T>(key: string, value: T, age?: number): Promise<void>` — Сохраняет значение в кэше.
- `setByFetch<T>(fetch: ApiFetch, value: T): Promise<void>` — Сохраняет значение на основе опций запроса.
- `remove(key: string): Promise<void>` — Удаляет запись вручную.

### Конфигурация

- `init(getListener, setListener, removeListener, cacheStepAgeClearOld?): void` — Настраивает кэш слушателями хранилища.
- `reset(): void` — Очищает элементы в памяти и отключает слушателей.

## Типы данных

#### `ApiCacheItem<T>`
Структура кэшированной записи.
- `value: T` — Значение данных.
- `age: number` — TTL в секундах.
- `cacheAge: number` — Метка времени создания (мс).

#### `ApiCacheList`
Внутренний словарь для резервного хранения в оперативной памяти.
- `[key: string]: ApiCacheItem` — Пары ключ-значение кэшированных элементов.

## Инициализация и внешнее хранилище

По умолчанию `ApiCache` сохраняет записи в оперативной памяти (RAM) процесса и работает только на стороне сервера. Хотя это очень быстро, в продуктовой среде такой подход имеет ограничения:
1. **Синхронизация в кластере**: В многоэкземплярной конфигурации кэши не разделяются между процессами.
2. **Перезагрузка процесса**: Память очищается при каждом перезапуск сервера или «холодном старте» серверных функций.
3. **Лимиты памяти**: Хранение больших объемов данных в RAM может привести к чрезмерному потреблению ресурсов.

Для решения этих задач необходимо инициализировать `ApiCache` с помощью метода `init`, предоставив слушателей (listeners) для связи с внешним механизмом хранения.

> Важно: Метод `ApiCache.init()` должен быть вызван на этапе запуска приложения (до первого API-запроса), чтобы гарантировать правильную конфигурацию кэша.

### Node.js (Произвольное хранилище)

В стандартной среде Node.js вы можете использовать слушателей для взаимодействия с централизованной базой данных или персистентным кэшем, таким как Redis.

```typescript
import { ApiCache } from '@dxtmisha/functional-basic'

// Конфигурация со слушателями хранилища
ApiCache.init(
  // 1. Получение данных из внешнего хранилища
  async (key) => await myExternalStorage.get(key),
  
  // 2. Сохранение данных во внешнее хранилище
  async (key, value) => {
    await myExternalStorage.put(key, value)
    return true // Возвращаем true для подтверждения внешнего сохранения
  },
  
  // 3. Удаление данных из внешнего хранилища
  async (key) => {
    await myExternalStorage.delete(key)
    return true
  },
  
  // 4. Опционально: порог срабатывания фоновой очистки
  16384
)
```

### Nuxt / Nitro

В Nuxt/Nitro рекомендуемый подход — использование встроенного хелпера `useStorage` внутри серверного плагина. Это позволяет задействовать Redis, файловую систему или любой другой драйвер, поддерживаемый Nitro.

```typescript
// server/plugins/api-cache.ts
import { ApiCache } from '@dxtmisha/functional-basic'

export default defineNitroPlugin(() => {
  const storage = useStorage('cache') // Регион кэша Nitro

  ApiCache.init(
    async (key) => await storage.getItem(key),
    async (key, value) => {
      await storage.setItem(key, value)
      return true
    },
    async (key) => {
      await storage.removeItem(key)
      return true
    }
  )
})
```
