CASE STUDY · 02
Q-Watcher
Movie and TV discovery with personal watchlists and ratings.
- Next.js 16
- React 19
- TypeScript
- TMDB API
- MariaDB
- Better Auth
- Tailwind CSS
No sign-up needed: browse and search the full catalog, or use the one-click demo account to try the watchlists.

Overview
Q-Watcher combines TMDB's movie and TV catalog with personal lists. Anyone can browse, search, filter, and open title pages and trailers; signed-in users can save titles to To Watch or Watched and give them their own rating.
The interesting part is integrating a third-party API with your own data: catalog data comes from TMDB, personal preferences come from MariaDB, and the two are merged on the server for each request.
Engineering decisions
- 01
Browsing state survives navigation
Going back from a title restores every loaded page and the exact scroll position.
Infinite scrolling usually breaks the back button: you return to page one at the top. Q-Watcher saves the loaded titles, page number, and scroll position for the current list URL in session storage, with an in-memory fallback when storage is unavailable. The saved state is validated before it is used, and when a user changes a rating on a detail page, the cached list is updated too, so returning never shows stale data.
- 02
Filters live in the URL
Search, genre, type, and list status are query parameters, so every view can be shared.
The list state is represented by query, genre, type, and status parameters. A shared or bookmarked link opens exactly the same view, the browser history works as expected, and the server can render the first page of results directly from the URL.
- 03
Two layers of access control
A cheap routing check in proxy.ts, plus full session validation on every write.
proxy.ts keeps the catalog, title pages, and account pages public and redirects guests away from private routes. That is only a routing convenience. Every preference write validates the Better Auth session, the media ID, the media type, and the rating on the server before touching the database. TMDB responses are cached for an hour, and a missing trailer never prevents a detail page from loading.
Architecture
- Rendering
- Async Server Components fetch the first catalog page and title details. Client Components handle filters, infinite scrolling, and preference controls.
- Integration
- TMDB REST API v3 for discovery, search, details, and trailers, with one-hour revalidation.
- Data
- MariaDB stores preferences keyed by user, TMDB ID, and media type, so a movie and a TV show with the same ID stay distinct.
- Auth
- Better Auth with email verification and password reset. Hooks prevent the demo account's credentials and sessions from being changed.
What I would build next
- Move the TMDB key to server-only configuration.
- Personal statistics: genres watched, average rating compared with TMDB.
- End-to-end tests for list restoration and the watchlist flows.