Когда фронтенд собирается статически, а бэкенд ещё не готов, встаёт классический вопрос: что считать источником правды во время разработки?

Мы решили эту задачу один раз — на уровне загрузчика коллекций, а не разбрасывая проверки if (isDev) по компонентам.

Зачем нужен единый свап-поинт#

Идея простая: у каждой коллекции есть ровно одна функция-источник. Сегодня она читает JSON-фикстуру, завтра — дергает Content API Laravel. Компоненты об этом не знают.

src/content/loaders/content-source.ts
export function contentSource(entity: ContentEntity): Loader {
  if (USE_FIXTURES) {
    return file(`src/content/fixtures/${entity}.json`);
  }
  return contentApiLoader(entity);
}

Почему не просто fetch() в компоненте

Loader выполняется один раз на сборку и результат кешируется Content Layer — на 40 страницах не будет 40 одинаковых запросов.

Что даёт схема Zod#

  • Ошибка формата ловится в момент astro build, а не после деплоя.
  • IDE знает точные поля коллекции — автодополнение работает как для обычного TypeScript.
  • Опциональные поля явно помечены — не нужно гадать, может ли значение быть null.
ИсточникКогда используетсяЗадержка сборки
Фикстуры (JSON)Локальная разработка, CI без бэкенда~0 мс
Content APIПродакшн-сборка, preview-окружениязависит от сети
Схема потока данных от Content API к статическим страницам Astro
Один и тот же контракт данных — что в фикстуре, что в ответе API.

Лучший рефакторинг — тот, который не потребовался, потому что архитектура заранее оставила для него один шов.

— из внутренней переписки команды

Что дальше#

В следующих раундах мы просто меняем USE_FIXTURES на false и реализуем contentApiLoader() — ни одна страница блога, услуг или проектов не потребует правок.

Ключевые выводы

  • Content Layer в Astro 5+ — универсальный интерфейс загрузчика, а не просто обёртка над файловой системой.
  • Один «свап-поинт» между фикстурами и реальным API избавляет страницы от условной логики.
  • Схема Zod ловит несовпадения формата данных на этапе сборки, а не в рантайме у пользователя.
  • Собственный Loader пишется один раз и переиспользуется для всех коллекций проекта.

Частые вопросы

Чем Content Layer отличается от старых Content Collections?

Старые Content Collections были жёстко привязаны к файлам в src/content. Content Layer вводит интерфейс Loader — источником данных может быть файл, API или база, а схема остаётся общей.

Нужно ли переписывать компоненты при смене источника данных?

Нет — если контракт (форма данных, описанная схемой) не меняется, компоненты продолжают работать без правок.

Нужен похожий контур для вашего проекта?

Спроектируем фронтенд так, чтобы бэкенд можно было подключить в любой момент без переписывания страниц.

Обсудить проект