All work

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.

q-watcher.next-nexus.devQ-Watcher catalog page with type, genre, and search filters above a grid of movie cards with titles and ratings (poster artwork blurred)

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

  1. 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.

  2. 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.

  3. 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.

HAVE SOMETHING IN MIND?

Let’s build something
that holds up.

Freelance projects, contract work, or a permanent role. I work remotely from Lithuania, in the EU time zone. Tell me what you are building.

LET’S CONNECT

Start a conversation.

Tell me about your project. I’ll reply to the email address you provide.

Your details are used to respond to your enquiry.