Project case study

Digital product / 2026

CineVault

A film for tonight. A collection for later.

A film-discovery and watchlist app: browse a curated collection or search the OMDb catalogue, read the details, and save a film without creating an account.

Explore live project
Project
Course final, independently rebuilt
Role
Interface Design + Front-End Development
Built with
React · Vite · Vercel function · OMDb API
Code
View on GitHub ↗
CineVault homepage with The Shawshank Redemption as the featured film
The live CineVault homepage. Film data and posters via OMDb.

01 / The brief

Less deciding. More discovering.

Film browsing turns into a loop of open tabs, forgotten titles and starting over. CineVault keeps finding a film, understanding it and saving it in one place, with no account required.

It began as my final project for the Frontend Simplified React course. I then rebuilt it on Vite and reworked the interface, the data layer and accessibility for my portfolio.

CineVault collection grid with ten film posters, ratings, genres and directors
Curated collection: posters, ratings, genre tags and one-tap saving on every card.

02 / The key decision

Move the API key off the browser.

The course version called OMDb straight from the browser, with the API key in the JavaScript. Anyone could read it in DevTools, and it ended up in the public git history.

In the rebuild the browser only calls my own endpoint, /api/movies. The server adds the key, validates the input, times out slow requests, shares identical requests and caches results for five minutes.

The trade-off: CineVault now needs a server runtime, so uploading static files alone no longer works. On Vercel that is one small function, and the same handler runs as Vite middleware during development.

src/services/omdb.js (before → after) ↗
// Before: the course version sent the key from every visitor's browser
const res = await fetch(`${BASE_URL}?apikey=${API_KEY}&i=${imdbID}&plot=${plot}`);

// After: the browser calls /api/movies; only the server knows the key
const BASE_URL = '/api/movies';
const res = await fetch(`${BASE_URL}?${new URLSearchParams(params)}`, { signal });

// server/movies.mjs
params.set('apikey', apiKey);
const response = await fetcher(`https://www.omdbapi.com/?${params}`, { signal: AbortSignal.timeout(10000) });
CineVault film detail dialog for The Shawshank Redemption with synopsis, cast, ratings and watchlist button
Details open in a native dialog that traps focus and returns it to the card that opened it.

03 / Product journey

Three steps. One place.

  • Discover through a curated collection or title search, with curated picks clearly separated from catalogue results.
  • Understand a film through its synopsis, cast, director, runtime and published ratings.
  • Keep a title from a card, the featured slideshow or the details view, then come back to the watchlist without signing up.
CineVault featured film on a phone
Phone: featured film
CineVault poster grid on a phone
Phone: two-column collection

04 / Engineering

Small components. Clear responsibilities.

  • React Context separates discovery, watchlist and theme state.
  • Superseded searches are cancelled, and collection loading runs four requests at a time while keeping the films that succeed.
  • Watchlist and theme persist in the browser, with an in-memory fallback when storage is blocked.
  • The featured slideshow starts paused and respects reduced-motion preferences.

05 / Verification

Tested where it can break.

Eight automated tests (Node’s built-in test runner) cover input validation, a missing key, caching, request sharing, upstream errors, partial failures, pagination and cancellation. The production build and tests pass on every change.

Film data and posters come from OMDb. No user-research results or business metrics are claimed.

Next: watch a few people find, save and revisit a film, then use what I learn to decide between filters and cross-device sync.

Explore the work

See the experience
in motion.