Budowanie strony z katalogiem brzmi banalnie, dopóki nie znajdziesz się w sytuacji, gdy musisz konfigurować bazy danych, warstwy buforowania i reaktywne frameworki front-endowe tylko po to, by wyświetlić coś, co w zasadzie jest starannie opracowanym spisem treści. Niedawno zbudowałem Social Tools List, stronę do porównywania oprogramowania do mediów społecznościowych. Moim celem było szybkie uruchomienie jej w sieci, zapewnienie szybkości działania i uniknięcie utrzymywania infrastruktury dla treści, które zmieniają się tylko wtedy, gdy dodaję lub aktualizuję dane narzędzie. Postawiłem na stos typu static-first: Astro do generowania strony, TypeScript do ustrukturyzowanych danych oraz Cloudflare Workers do wdrożenia. Rezultatem jest strona, która ładuje się natychmiastowo, kosztuje niemal zero w utrzymaniu i nie wymaga administracji bazą danych.

Dlaczego podejście static-first ma sens w przypadku katalogu

Wiele aplikacji webowych domyślnie korzysta z renderowania po stronie serwera lub architektur typu single-page, ponieważ wydają się one bezpiecznymi, nowoczesnymi wyborami. Jednak nie każda strona otrzymuje dynamiczne dane od użytkownika przy każdym zapytaniu. Social Tools List to zasób zorientowany na odczyt (read-heavy). Dane porównawcze zmieniają się, gdy wypycham aktualizację, a nie gdy odwiedzający odświeża stronę. Renderowanie HTML z wyprzedzeniem eliminuje potrzebę wykonywania zapytań do bazy danych na brzegu sieci (at the edge), kompilacji szablonów w locie czy narzutu związanego z hydracją w przeglądarce. Generuję stronę w czasie budowania (build time), wdrażam statyczne pliki i pozwalam lekkiej funkcji worker na obsługę opakowania (wrapper). Dzięki temu czasy odpowiedzi są niskie, a cała klasa błędów w czasie działania (runtime) zostaje wyeliminowana.

Przechowuj dane w TypeScript, a nie w bazie danych

Nie używam bazy danych. Każde narzędzie w katalogu jest zdefiniowane jako obiekt TypeScript z polem slug, nazwą, domeną i tablicą obsługiwanych procesów (workflows). Typowy wpis wygląda tak:

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

Przechowywanie danych w tym samym języku, który buduje stronę, niesie ze sobą dwie natychmiastowe korzyści. Po pierwsze, pull requesty stają się przeglądem treści. Gdy dodaję narzędzie, diff pokazuje dokładne pola i wartości, a współpracownik może wyłapać literówkę lub błędną domenę bez konieczności nauki interfejsu CMS. Po drugie, kompilator TypeScript wymusza strukturę każdego rekordu. Jeśli zapomnę dodać slug lub pomylę klucz workflow, proces budowania zakończy się błędem, zanim błędne dane trafią na stronę.

Baza danych wprowadziłaby migracje, ciągi połączeń (connection strings), strategie buforowania i procedury kopii zapasowych. W przypadku katalogu z kilkuset wpisami, które utrzymuję ręcznie, taki narzut jest czystym obciążeniem. Statyczne dane w modułach TypeScript to najtańszy i poprawny wybór dla tego modelu. Część dotycząca „poprawności” jest istotna. Nie chodzi tylko o oszczędność pieniędzy, ale o usunięcie warstw abstrakcji, które rozwiązują problemy, których nie mam.

Pozwól Astro przejąć routing i renderowanie

Astro generuje jedną stronę HTML dla każdego narzędzia na podstawie pojedynczej dynamicznej trasy. Definiuję jeden komponent układu (layout), a Astro automatycznie tworzy metadane, nagłówki i dane ustrukturyzowane dla każdego wpisu. Ponieważ ten sam zestaw danych napędza główny indeks, centra kategorii workflow oraz poszczególne strony szczegółowe, nie ma możliwości, aby karta na stronie głównej wyświetlała inny opis niż sama strona szczegółowa. W tradycyjnych konfiguracjach CMS często dochodzi do rozbieżności: API zwraca jedną wersję, cache drugą, a renderowanie po stronie klienta trzecią. Generowanie statyczne z jednego źródła prawdy (single source of truth) zapobiega temu zjawisku.

Architektura wysp (islands architecture) Astro ułatwia również dodawanie małych interaktywnych elementów bez „zatruwania” całej strony przez JavaScript. Strona jest dostarczana jako statyczny HTML, a jedynie skrypt filtrowania przeprowadza hydrację w swoim konkretnym fragmencie DOM. Nie ma żadnego runtime'u frameworka otaczającego cały dokument. Astro traktuje metadane strony jako element pierwszej klasy. Każda strona narzędzia otrzymuje własny tag title i meta description wygenerowany bezpośrednio z rekordu TypeScript, więc nie potrzebuję osobnego pluginu ani biblioteki do zarządzania sekcją head.

Filtrowanie bez frameworka

Wyszukiwanie i filtrowanie często skłaniają programistów do instalowania Reacta, Vue lub ciężkich bibliotek do zarządzania stanem. Ja temu się oparłem. Astro renderuje pełną listę jako zwykły HTML na serwerze. Mały skrypt w czystym JavaScript, ważący mniej niż kilobajt, działa w przeglądarce i przełącza właściwość display elementów listy na podstawie tagu workflow lub dopasowania tekstu.

Serwowanie pełnego kodu znaczników może brzmieć nieefektywnie, jeśli masz doświadczenie w pracy opartej na API. Ale weź pod uwagę narzut typowego podejścia dynamicznego. Przeglądarka pobiera paczkę JavaScript, przeprowadza hydratację drzewa komponentów, wywołuje endpoint, czeka na JSON, a następnie renderuje wiersze. W przypadku katalogu zawierającego mniej niż sto narzędzi, ten rytuał jest wolniejszy i mniej niezawodny niż ukrywanie elementów div, które znajdują się już w dokumencie. Mój skrypt przypisuje słuchaczy zdarzeń do przycisków filtrowania, odczytuje atrybut data-workflow w każdym wierszu i ustawia elementy niepasujące jako ukryte. Operacja ta zajmuje milisekundy.

Ponieważ lista jest obecna w początkowym kodzie HTML, strona jest użyteczna bez JavaScriptu. Roboty wyszukiwarek widzą każdy link i każdy opis. Użytkownicy na wolnych łączach lub korzystający z blokerów skryptów wciąż mają dostęp do pełnego katalogu. Filtrowanie jest ulepszeniem, a nie barierą.

Sitemapy i pliki robots jako kod

Sitemapy i pliki robots.txt nie są jedynie dopisywanymi ręcznie dodatkami. To trasy Astro, które korzystają z tego samego zbioru danych i funkcji pomocniczych URL co