Movie Explorer is a modern React + TypeScript web app for discovering trending, top-rated, and lesser-known movies via the TMDB (The Movie Database) API.
Built with Ant Design and styled-components, it features a dark/light theme, server-side filtering, infinite scrolling, a persistent watchlist, and AI-generated movie summaries powered by OpenAI. Both the TMDB and OpenAI keys stay server-side behind Vercel serverless proxies — no keys in the browser.
- Search — Debounced search-as-you-type across the full TMDB catalogue.
- Discover & filter — Browse trending movies by default, or filter the whole catalogue server-side by genre, release year, and minimum rating, sorted by popularity, release date, or rating (with a vote-count floor to keep out junk ratings).
- Infinite scrolling — Results load page-by-page as you scroll, powered by TanStack Query's
useInfiniteQuerywith deduplication and previous-results kept on screen while new ones load. - AI summaries — Each movie's detail page shows a spoiler-free, bullet-point summary generated by OpenAI (
gpt-4o-mini), with copy-to-clipboard. The OpenAI key never leaves the server. - Rich movie details — Top cast, similar movies, YouTube trailer link, and where-to-watch providers, all from a single TMDB request.
- Watchlist — Save movies locally (localStorage), then search, sort (recently added / title / rating / year), export to JSON, or clear.
- Dark / light theme — One toggle drives both Ant Design's theme algorithm and the styled-components theme via a shared token bridge.
| Layer | Tech |
|---|---|
| Frontend | React 18, TypeScript, React Router 6 |
| UI | Ant Design 6, styled-components 6, react-icons |
| Data fetching | TanStack Query 5, Axios |
| APIs | TMDB, OpenAI |
| Backend | Vercel serverless functions (proxy TMDB and OpenAI, keys server-side) |
| Build | Vite 5 |
| Hosting | Vercel |
movie_explorer/
├── client/
│ ├── api/
│ │ ├── summary.js # Vercel serverless function — OpenAI proxy (rate-limited, key stays server-side)
│ │ └── tmdb.js # Vercel serverless function — TMDB proxy (key stays server-side)
│ ├── public/ # robots.txt, sitemap.xml
│ ├── src/
│ │ ├── api/ # TMDB client, endpoints, shared types
│ │ ├── components/ # Header, MovieCard, MovieGrid, FilterBar, SearchBar, ...
│ │ ├── hooks/ # useMovies, useWatchlist, useThemeMode, useDebouncedValue, ...
│ │ ├── pages/ # Home, MovieDetail, Watchlist
│ │ ├── styles/ # Theme tokens, global styles, styled-components typings
│ │ ├── App.tsx
│ │ └── main.tsx
│ ├── index.html
│ ├── vercel.json # Security headers, SPA fallback, TMDB proxy rewrite
│ ├── vite.config.ts
│ └── tsconfig.json
└── docs/ # Design specs
| Variable | Where | Purpose |
|---|---|---|
TMDB_API_KEY |
client/.env + Vercel project settings |
TMDB key, used only by the TMDB proxy (and the vite dev proxy locally) |
OPENAI_API_KEY |
Vercel project settings | OpenAI key, used only by the summary function |
Create client/.env with:
TMDB_API_KEY=your_tmdb_api_key
Neither key uses a VITE_ prefix — both are read server-side (or by the vite dev proxy at startup), so they're never bundled into the client. All browser TMDB traffic goes through /api/tmdb, and /api/summary is rate-limited (10 req/min per IP) to prevent abuse.
cd movie_explorer/client
npm ci
npm run devOther scripts: npm run build (type-check + production build), npm run typecheck.
In local dev, TMDB requests are handled by a vite proxy that reads
TMDB_API_KEYfromclient/.env. AI summaries require the serverless function, so they only work when deployed to Vercel (or viavercel dev) withOPENAI_API_KEYset.
- Authenticated user accounts with a backend-synced watchlist
- Personalised movie recommendations
- TV series support