ReelSearch is a full-stack web application for discovering movies, TV series, and games through the OMDb catalog. Users can search the catalog, sort and filter results by rating, type, and year, explore detailed metadata with aggregated critic scores, and maintain a persistent personal watchlist.
Frontend:
reelsearch-ten.vercel.app
Backend health check:
https://reelsearch-api-f3qb.onrender.com/health
Search results display key movie metadata and allow users to add or remove titles from their watchlist.
Saved titles are displayed in a poster grid with sort and filter controls. Items can be removed or opened for full details.
Clicking any poster card opens a full detail overlay with plot, cast, director, and critic scores from IMDb, Rotten Tomatoes, and Metacritic.
The profile page displays account info alongside watchlist stats computed from saved titles.
- Frontend
- React
- Vite
- React Router
- Tailwind CSS v4
- Backend
- Node.js
- Express
- Prisma
- Database
- PostgreSQL (Neon)
- Authentication
- bcrypt, JWT (stored in an httpOnly cookie)
- External API
- OMDb API (movie data)
- OpenAI API (natural language watchlist queries)
- Deployment
- Vercel (frontend)
- Render (backend)
ReelSearch follows a typical full-stack architecture:
- The React + Vite frontend communicates with an Express REST API
- The API handles authentication, search requests, and watchlist management
- Search requests are proxied to the OMDb API, and the server fetches full metadata for each result
- Natural language watchlist queries are parsed using the OpenAI API and converted into structured database filters
- Prisma ORM manages database access for user accounts and watchlists
- Watchlist data is stored in PostgreSQL (Neon)
- Authentication is implemented with JWT tokens stored in secure httpOnly cookies
reelsearch/
├─ client/ # React + Vite frontend
│ ├─ src/
│ │ ├─ App.jsx
│ │ ├─ index.css # Tailwind v4 theme tokens and global styles
│ │ ├─ pages/
│ │ │ ├─ Home.jsx
│ │ │ ├─ Login.jsx
│ │ │ ├─ Profile.jsx
│ │ │ ├─ Register.jsx
│ │ │ └─ Watchlist.jsx
│ │ ├─ components/
│ │ │ ├─ AuthForm.jsx # Shared login/register form
│ │ │ ├─ Dropdown.jsx # Accessible sort/filter dropdown
│ │ │ ├─ ItemDetail.jsx # Full-screen detail overlay
│ │ │ ├─ MediaGrid.jsx # Shared poster grid with sort/filter toolbar
│ │ │ ├─ PosterCard.jsx # Individual poster card with rating badge
│ │ │ ├─ SearchBar.jsx
│ │ │ └─ WatchlistChat.jsx
│ │ ├─ utils/
│ │ │ ├─ mediaHelpers.js # Sort/filter logic and rating derivation
│ │ │ └─ normalizeMovie.js # Shapes watchlist API responses for the UI
│ │ └─ services/ # Frontend API wrappers (auth, search, watchlist)
│ ├─ vercel.json # Rewrites /api/* to the Render backend in production
│ └─ vite.config.js
│
├─ server/ # Express backend (API, auth, watchlist features)
│ ├─ index.js # Express entry point
│ ├─ db.js # Prisma client initialization
│ ├─ lib/
│ │ └─ openai.js # OpenAI client setup
│ ├─ routes/
│ │ ├─ auth.routes.js
│ │ ├─ chat.routes.js # Natural language watchlist queries
│ │ ├─ search.routes.js
│ │ └─ watchlist.routes.js
│ ├─ services/
│ │ ├─ omdb.service.js # OMDb API proxy logic
│ │ ├─ chat.service.js
│ │ └─ watchlistQuery.service.js
│ ├─ middleware/
│ │ └─ requireAuth.js # JWT cookie authentication middleware
│ └─ utils/
│ ├─ normalizeSearchResult.js # Normalizes OMDb search results before sending to client
│ ├─ ratings.util.js
│ └─ serializeWatchlistItem.util.js
│
├─ prisma/
│ ├─ schema.prisma # Prisma schema defining User &Watchlist models
│ └─ migrations/ # Generated Prisma migrations
│
├─ package.json # Root scripts for dev / server / client
└─ README.md
Search the OMDb catalog by title and return enriched metadata for each result.
- Results display key information such as poster, title, year, type, and age rating for quick browsing
- The backend fetches full metadata for each unique result before returning it to the client
- Results can be sorted by relevance, rating, title, or release year, and filtered by content type (Movies, Series, or Games)
- Clicking a poster card opens a detail overlay with plot, director, cast, and individual scores from IMDb, Rotten Tomatoes, and Metacritic
- Users can add titles directly to their watchlist from the search results
Create an account and securely manage a personal watchlist.
- Users can register and log in with email and password
- Passwords are securely hashed using
bcrypt - Authentication is handled through a signed JWT stored in a secure
httpOnlycookie - Logged-in users can view their profile via the avatar icon in the header → Profile, which shows their email, member since date, and watchlist stats
- The profile page displays watchlist stats computed from saved titles: total items, composite average rating, and a breakdown by Movies, Series, and Games
Save titles to a personal watchlist backed by PostgreSQL.
- Movies, series, and games can be added directly from search results
- Watchlist items are stored per user using Prisma ORM and PostgreSQL
- Items can be removed from the watchlist at any time Clicking a poster card opens a detail overlay with plot, director, cast, and critic ratings
- Watchlist items can be sorted and filtered using the same controls as search results
- A built-in assistant lets users filter and sort their watchlist using plain-English queries such as "show my sci-fi movies after 2015," powered by the OpenAI API
Normalize critic ratings from multiple sources into a single comparable score.
- OMDb ratings (IMDb, Rotten Tomatoes, Metacritic) are normalized to a 0–100 scale and averaged into a composite
sortScore - The composite score is displayed as a star badge (0–10) on every poster card
- Individual scores from all three sources are shown in full inside the item detail overlay
- The
sortScorealso powers the "Rating" sort option on both the search results and watchlist grids
- Node.js (version 18 or higher is recommended)
- npm (bundled with Node.js)
- A PostgreSQL database URL (e.g. via Neon, Supabase, local PostgreSQL)
- An OMDb API key (free: https://www.omdbapi.com/apikey.aspx)
- An OpenAI API key (https://platform.openai.com/)
-
Fork or Clone the Repository
git clone https://github.com/abj32/reelsearch.git cd reelsearch -
Install Dependencies
Install backend dependencies:npm install
Then install frontend dependencies:
cd client npm install cd ..
-
Set Up Environment Variables
Create a.envfile in the project root and define:# PostgreSQL connection string used by Prisma DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=require" # Secret key used to sign JWTs JWT_SECRET="a-long-random-string-here" # OMDb API key (used only on the backend) OMDB_API_KEY="your_omdb_api_key_here" # OpenAI API key (used for chat-based watchlist queries) OPENAI_API_KEY="your_openai_api_key_here" # Optional: port for the Express API (default is 5000) PORT=5000 # Optional: set on your Render backend to allow requests from your production frontend CLIENT_URL="https://your-frontend.vercel.app" # Optional: set on your Render backend to allow Vercel preview deployment URLs # Use a regex matching your preview URL pattern, e.g. "https://reelsearch-.*\.vercel\.app" VERCEL_PREVIEW_ORIGIN_REGEX="your-regex-here"
-
Running the Development Server
From the project root run:npm run dev
This uses
concurrentlyto start both- the Express API (
npm run start-server) on http://localhost:5000 - the Vite dev server (
npm run start-client) on http://localhost:5173
- the Express API (
Once npm run dev is running:
- Open the frontend: http://localhost:5173
- Register/Log In
- Note: You do not need to be logged in to search for and view movies/shows/games, but you do need to be logged in to add items to your watchlist
- Click the profile icon in the header and go to Register or Login
- Create an account with email and password or login with an existing account
- After successful registration or login, the server sets an
httpOnlysession cookie (sid) with a signed JWT (expires in 1 hour) - The client automatically fetches your profile on load (
GET /api/auth/profile) to check for session cookie and JWT - You can log out via Profile Icon -> Log out
- Search for Titles
- Use the search bar in the header to search by title
- The frontend calls the backend’s search endpoint (
GET /api/search?q=<query>) - The backend then:
- Calls OMDb using your API_KEY
- Deduplicates results
- Fetches full details for each unique imdbID
- Normalizes ratings from IMDb, Rotten Tomatoes, and Metacritic into a composite score
- Returns a list of enriched movie objects to the client
- Managing Your Watchlist
- After searching, click the “+” button on a card to add it to your watchlist; saved titles show a trash icon that removes them from your watchlist
- The frontend calls:
POST /api/watchlistwith{ "imdbId": "<imdbID>" }
- The item is stored in your watchlist in the database with a relation to your userId
- Visit the Watchlist page via the bookmark icon in the header to browse saved items, view full details, and remove titles
API errors return a consistent JSON shape:
{
"code": "MACHINE_READABLE_ERROR_CODE",
"message": "Human-readable error message"
}GET /health
Simple health check:{ "ok": true }
POST /api/auth/register
Body:{ "email": string, "password": string }
Creates a new user, setssidcookie, returns the authenticated user’sid,email, andcreatedAtPOST /api/auth/login
Body:{ "email": string, "password": string }
Verifies credentials, setssidcookie, returns the authenticated user’sid,email, andcreatedAtPOST /api/auth/logout
Clears thesidcookieGET /api/auth/profile
Requires auth (requireAuthmiddleware) Returns the authenticated user’sid,email, andcreatedAt
GET /api/search?q=<query>
Calls OMDb using the backend's API_KEY, deduplicates results, fetches full metadata for each unique result, normalizes ratings into a compositesortScore, and returns an array of enriched movie objects
Note: All watchlist routes require authentication (requireAuth middleware)
GET /api/watchlist
Returns the current user's watchlist itemsPOST /api/watchlist
Body:{ "imdbId": string }- Fetches full details from OMDb
- Normalizes ratings
- Stores the item for the current user
DELETE /api/watchlist/:imdbId- Removes specific item from the user’s watchlist
Note: Requires authentication (requireAuth middleware)
-
POST /api/chat/watchlist
Body:{ "message": string }- Parses natural language input using OpenAI
- Converts the request into structured filters and sorting options
- Returns filtered/sorted watchlist items
Example:
{ "message": "show my sci-fi movies after 2015" }
- 🤖 AI-Powered Content Discovery
- Discover movies, TV series, and games using natural language prompts
- Receive personalized recommendations based on genres, themes, actors, directors, moods, and viewing preferences
- Examples:
- "Recommend a sci-fi movie like Interstellar"
- "What are some suspenseful mystery series?"
- "Suggest a comedy that's good for family movie night"
- ⭐ Watchlist Insights & Recommendations
- Leverage stored metadata and normalized critic scores for smarter ranking and recommendation features
- 📝 Issue-driven refinements:
- Smaller UX and styling improvements tracked under the Issues tab.
Stay tuned for updates!
MIT



