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 ↗

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.

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.
// 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) });
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.


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