TheBook&FilmClub
Privé webapp voor mij en mijn partner om samen boeken, films en series bij te houden, met individuele scores en recensies per titel.
- Zoeken (Open Library)
- Toevoegen aan plank
- Score & recensie
- Aanbeveling (Groq)
Probleem
Geen bestaande app deed precies wat wij wilden: samen bijhouden wat we lezen en kijken, met een gedeelde verlanglijst, maar wél ieder onze eigen score en recensie per titel in plaats van één gedeelde mening.
Dit is een privéproject voor persoonlijk gebruik. Daarom staat hier geen link naar de live omgeving of de broncode, alleen de case study.
Aanpak
Server-rendered Thymeleaf met een handgeschreven CSS-designsysteem, mobile-first met een vaste bottom-navigatie en licht/donker-modus. Nieuwe titels voeg je zoek-first toe (Goodreads-stijl) via de Open Library-API.
Onder water: Spring Boot 3.3.5 (Web, Security, Data JPA, Validation), PostgreSQL via Supabase in productie en H2 lokaal, Spring Security met BCrypt voor authenticatie, en Groq (Llama) voor aanbevelingen op basis van eerdere beoordelingen. Het datamodel is bewust symmetrisch opgezet, zodat een derde gebruiker of nieuwe functionaliteit er zonder herontwerp bij kan. Auto-deploy vanuit git naar Railway.
In de praktijk
Stack
Wat ik ervan leerde
Twee productiebugs kwamen naar boven bij de migratie van Railway naar Supabase, en die vertellen het beste over hoe ik werk.
1. De gemaskeerde connectiefout. Een verwijderde Railway Postgres-plugin brak de databaseverbinding stilletjes. De app toonde "ongeldige inloggegevens", terwijl de werkelijke oorzaak een onderliggende connectiefout was, gemaskeerd door de generieke foutafhandeling van Spring Security. Dat liet zien dat foutafhandeling een probleem kan verbergen in plaats van tonen. Naar de gebruiker toe wil je precies dat: één vage melding, niets lekken over wat er misging. Maar in je logs wil je het tegenovergestelde, namelijk het volledige, specifieke verhaal.
2. function lower(bytea) does not exist. Na de overstap naar
Supabase's connection pooler brak de zoekfunctie met deze cryptische
Postgres-foutmelding. De oorzaak bleek parameter-type-inferentie: de
pooler kon het parametertype niet meer afleiden zoals de directe
verbinding dat deed. Opgelost met een expliciete cast(... as string) in
de JPQL-query.
Beide bugs heb ik opgelost door productielogs te lezen, de daadwerkelijke oorzaak te achterhalen in plaats van de symptomen, en de fix vervolgens live te deployen. Precies het soort werk dat in tutorials niet aan bod komt.