Webová stránka ve vývoji
Next.jsStrapiHeadless CMS
Article written: 27 May 2025Development period: 08/2024 - 04/2025Reading time: 5 min

Virtuální knihovna

TODO un-llm the translation

t

Tento projekt vznikl jako praktická část mé bakalářské práce a představuje můj první skutečný pokus o full-stack aplikaci. Výrazně jsem při něm čerpal z principů redakčních systémů, se kterými jsem se seznámil během stáže. Zatímco samotná práce se zabývala specifickou analýzou výkonu headless CMS, zde se zaměřím na širší architektonická rozhodnutí, funkce a klíčové poznatky. Zdrojový kód je k dispozici na GitHubu.

Architektura systému

Vzhledem k cílům práce bylo nutné navrhnout aplikaci s oddělenou (decoupled) architekturou. Pro srozumitelnost jsem systém rozdělil do tří vrstev: aplikační, redakční (CMS) a datové. Následující diagram znázorňuje tok informací a odpovědnosti jednotlivých vrstev.

Použité technologie

Volba technologií byla částečně dána cíli mé bakalářské práce. Jak je vidět z diagramu, pro aplikační vrstvu jsem použil Next.js. Vyhovuje mi práce s App Routerem a konceptem Server Actions. Jak jsem předpokládal, podpora pro ISR (Incremental Static Regeneration) je pro weby napojené na CMS naprosto klíčová. Nápad použít Strapi jako redakční systém vzešel ze stáže, kde jsem s ním krátce pracoval.

Později, abych vyřešil problémy s latencí, které jsem pozoroval při nasazení Strapi na Heroku (zejména na stránce s výpisem knih), integroval jsem dodatečně React Query. To zjednodušilo správu stavu a umožnilo mi lépe pracovat se synchronizací dat na pozadí a optimistickými aktualizacemi. Pro stylování jsem nakonec použil Tailwind CSS v kombinaci s shadcn/ui, což mi usnadnilo tvorbu konzistentního a dobře vypadajícího uživatelského rozhraní.

Klíčové funkce

  • Uživatelské účty: Aplikace využívá JWT pro autentizaci. Pomocí Next.js middleware jsem chránil stránky, takže pouze přihlášení uživatelé mají přístup ke své sekci. Shodou okolností byla zrovna v době, kdy jsem projekt prezentoval, v Next.js middleware objevena kritická zranitelnost, která mou implementaci dočasně vyřadila z provozu.

  • Uživatelský panel: Po přihlášení si mohou uživatelé spravovat svůj profil, včetně změny profilového obrázku, kde nahrávání a optimalizaci řeší Strapi. Dále zde mohou spravovat seznam svých „oblíbených“ knih a sledovat související statistiky.

  • Katalog knih: Uživatelé mohou procházet katalog knih s detaily jako autor, žánr a popis. Rozhraní zahrnuje stránkování a možnosti řazení. Každá kniha má vlastní stránku s podrobnějšími informacemi a komentáři od uživatelů.

  • Dynamický obsah: Úvodní stránka, stejně jako mnoho dalších částí aplikace, je plně spravována přes Strapi. To znamená, že administrátor může měnit obsah bez nutnosti zasahovat do kódu.

Implementace

Autentizace

Velkou výzvou byla autentizace. Kvůli složitosti validace JWT tokenů ze Strapi přímo v Next.js middleware jsem implementoval vlastní ověřovací most. Ten využíval dedikované API endpointy a následně vydával samostatný, session-based token, se kterým už middleware mohl bezpečně pracovat.

Nasazení

Nasazení aplikace byla tvrdá lekce z infrastruktury. Původně jsem pro Strapi zvolil Heroku, ale nedostatečná podpora pro monorepo a znatelné problémy s latencí mě donutily hledat alternativu. Po krátkém a náročném experimentu se Strapi Cloud, který se ukázal pro osobní projekt jako příliš drahý, jsem objevil Railway. Jeho bezproblémová integrace s Gitem, štědrý bezplatný tarif a celkově skvělý vývojářský zážitek byly generačním skokem vpřed a finální nasazení proběhlo hladce a efektivně.

Caching

Výrazně jsem podcenil a nepochopil caching. Vzhledem k tomu, že cachování bylo hlavním tématem mé práce a během vývoje prošlo významnými změnami, mé pokusy o optimalizaci výkonu se staly cennou lekcí.

Moje předchozí chápání cachingu bylo poměrně lineární; představoval jsem si ho jako jednu vrstvu nebo kolečko v soukolí aplikace. V tomto projektu jsem si však uvědomil, že se jedná o mnohem komplexnější téma, které vyžaduje pečlivé plánování. Nakonec jsem v aplikaci implementoval tři různé úrovně cachingu.

Server-side cache v Next.js zrychluje doručování stránek ukládáním již vyrenderovaných stránek, client-side cache v React Query zajišťuje rychlé a plynulé aktualizace dat v rámci aplikace a standardní cachování v prohlížeči snižuje zbytečné síťové požadavky na statické soubory. Společně tyto vrstvy vytvořily komplexní strategii cachingu, ačkoliv jejich koordinace se ukázala být náročnější, než jsem původně očekával.

Co jsem se naučil a co bych udělal jinak

Původně jsem veškerý stav spravoval pomocí useState a useContext z Reactu. Jak si asi dokážete představit, brzy se to stalo příliš složitým. Zavedení React Query bylo záchranou a u podobného projektu bych ho příště použil od samého začátku. I s mými omezenými zkušenostmi výrazně zjednodušil správu serverového stavu.

V tomto projektu jsem nepokládal důraz na testování, čehož lituji. V budoucích projektech bych chtěl implementovat jednotkové testy pro kritické komponenty a integrační testy pro API endpointy, abych zajistil spolehlivost. Zpětně bych určitě nějaké jednotkové testy napsal, i kdyby jen pro ten dopaminový pocit z jejich úspěšného splnění. Také, jelikož se má bakalářská práce silně zaměřovala na výkon headless CMS, vedlo mě to k přílišnému řešení některých aspektů výkonu, které nebyly pro jádro aplikace kritické. A výsledky navíc nebyly příliš průkazné.

Asi nejdůležitější lekcí bylo přemýšlet dopředu a navrhovat si nápady a architekturu v diagramech ještě před začátkem kódování. Tady jsem to (většinou) nedělal, což vedlo k některým neuspořádaným částem kódu a architektonickým rozhodnutím, která jsem musel později refaktorovat.

Závěrem

Celkově pro mě byla tvorba Virtuální knihovny velmi zábavnou a formativní zkušeností. Díky ní jsem si mohl spojit znalosti z různých oblastí webového vývoje, které jsem za poslední dva roky nasbíral, do jedné ucelené aplikace. Zároveň mi odhalila mé zásadní mezery v chápání některých témat a utvrdila mě v tom, jak důležité je plánování a architektura při vývoji softwaru.