Створення сайту-каталогу здається тривіальним заняттям, поки не виявиш, що доводиться підключати бази даних, рівні кешування та реактивні фронтенд-фреймворки лише для того, щоб відобразити те, що, по суті, є кураторським змістом. Нещодавно я створив Social Tools List — сайт для порівняння програмного забезпечення для соціальних мереж. Моєю метою було швидко запустити його, забезпечити швидкість роботи та уникнути підтримки інфраструктури для контенту, який змінюється лише тоді, коли я додаю або оновлюю інструмент. Я зупинився на підході static-first: Astro для генерації сайту, TypeScript для структурованих даних та Cloudflare Workers для розгортання. Результатом став сайт, який завантажується миттєво, майже не потребує витрат на хостинг і не потребує адміністрування баз даних.

Чому підхід static-first має сенс для каталогу

Багато вебдодатків за замовчуванням використовують серверний рендеринг або односторінкові архітектури, оскільки вони здаються безпечним і сучасним вибором. Але не кожен сайт отримує динамічні дані від користувача при кожному запиті. Social Tools List — це ресурс із переважним читанням (read-heavy). Дані для порівняння змінюються, коли я публікую оновлення, а не коли відвідувач оновлює сторінку. Попередній рендеринг HTML усуває потребу в запитах до бази даних на edge-серверах, компіляції шаблонів «на льоту» або накладних витратах на гідрацію в браузері. Я генерую сайт під час збірки, розгортаю статичні файли та дозволяю легкому worker-у обробляти обгортку. Це забезпечує низький час відгуку та усуває цілий клас помилок під час виконання (runtime failures).

Зберігайте дані в TypeScript, а не в базі даних

Я не використовую базу даних. Кожен інструмент у каталозі визначений як об'єкт TypeScript із slug, назвою, доменом та масивом підтримуваних workflow. Типовий запис виглядає так:

{
  slug: 'buffer',
  name: 'Buffer',
  domain: 'buffer.com',
  workflows: ['scheduling', 'analytics']
}

Зберігання даних тією ж мовою, якою будується сайт, дає дві миттєві переваги. По-перше, pull requests перетворюються на перевірку контенту. Коли я додаю інструмент, diff показує точні поля та значення, і колега може помітити друкарську помилку або неправильний домен, не вивчаючи інтерфейс CMS. По-друге, компілятор TypeScript забезпечує відповідність структури кожного запису. Якщо я забуду додати slug або помилюся в ключі workflow, збірка завершиться помилкою ще до того, як некоректні дані потраплять на сторінку.

База даних принесла б із собою міграції, рядки підключення, стратегії кешування та процедури резервного копіювання. Для каталогу з кількома сотнями записів, які я підтримую вручну, ці накладні витрати є лише зайвим тягарем. Статичні дані в модулях TypeScript — це найдешевший і правильний вибір для такої моделі. Частина про «правильність» має значення. Справа не лише в економії грошей, а й у видаленні рівнів абстракції, які вирішують проблеми, яких у мене немає.

Дозвольте Astro керувати маршрутизацією та рендерингом

Astro генерує одну HTML-сторінку для кожного інструменту на основі одного динамічного маршруту. Я визначаю один компонент макета (layout), а Astro автоматично створює метадані, заголовки та структуровані дані для кожного запису. Оскільки один і той самий набір даних керує головною сторінкою, хабами категорій workflow та окремими сторінками деталей, немає ризику того, що картка на головній сторінці показуватиме інший опис, ніж сама сторінка деталей. У традиційних налаштуваннях CMS часто спостерігається розбіжність: API повертає одну версію, кеш — іншу, а клієнтський рендеринг — третю. Статична генерація з єдиного джерела істини (single source of truth) запобігає цьому.

Архітектура островів (islands architecture) Astro також дозволяє легко додавати невеликі інтерактивні елементи, не захаращуючи всю сторінку JavaScript-кодом. Сайт постачається як статичний HTML, і лише скрипт фільтрації виконує гідрацію своєї частини DOM. Весь документ не охоплюється середовищем виконання (runtime) фреймворка. Astro також розглядає метадані сторінок як пріоритетне завдання. Кожна сторінка інструменту отримує власний тег title та meta description, отримані безпосередньо з запису TypeScript, тому мені не потрібен окремий плагін або бібліотека для керування тегом <head>.

Фільтрація без фреймворка

Пошук і фільтрація часто спонукають розробників встановлювати React, Vue або важку бібліотеку для керування станом. Я цьому протистояв. Astro рендерить повний список як звичайний HTML на сервері. Невеликий скрипт на чистому (vanilla) JavaScript розміром менше кілобайта працює в браузері та перемикає властивість display елементів списку на основі тегу workflow або текстової відповідності.

Передача повної розмітки може здатися неефективною, якщо ви звикли до підходу, орієнтованого на API. Але розгляньте накладні витрати типового динамічного підходу. Браузер завантажує JavaScript-бандл, виконує гідрацію дерева компонентів, викликає ендпоінт, чекає на JSON, а потім рендерить рядки. Для каталогу, що містить менше ста інструментів, цей ритуал є повільнішим і менш надійним, ніж приховування div, які вже є в документі. Мій скрипт додає слухачі подій до кнопок фільтрації, зчитує атрибут data-workflow у кожному рядку та встановлює hidden для тих, що не підходять. Ця операція займає мілісекунди.

Оскільки список присутній у початковому HTML, сайт залишається придатним для використання без JavaScript. Пошукові роботи бачать кожне посилання та кожен опис. Користувачі на повільних мережах або з блокувальниками скриптів все одно отримують повний каталог. Фільтрація — це покращення, а не бар'єр.

Sitemaps та robots як код

Sitemaps та robots.txt — це не другорядні речі, написані вручну. Це маршрути Astro, які використовують той самий набір даних і допоміжні функції URL, що й