A Netflix-inspired streaming platform with a cinematic UI featuring auto-playing billboard hero, hover-expanding movie cards, a favorites system ('My List'), full-screen video player, and multi-provider authentication. Built with Next.js 14 App Router, Prisma ORM with MongoDB, NextAuth.js (JWT sessions with credentials, Google, and GitHub OAuth), and SWR for client-side data fetching.
How the system is structured from frontend to external services.
Server Components for protected page shells with session checks and redirects. Client Components for the interactive browse experience — billboard with auto-play video, hover-expanding movie cards with CSS transforms, and the info modal. SWR handles all client-side data fetching with aggressive caching. Zustand manages the info modal state (open/close with movieId).
RESTful API routes handling movie listing, random movie selection, and favorites management. Every protected endpoint calls serverAuth() which uses getServerSession to verify the JWT token and fetch the current user from the database. Registration endpoint hashes passwords with bcrypt (10 salt rounds) and checks for duplicate emails.
Five models: User, Account, Session, VerificationToken, and Movie. Uses MongoDB's ObjectId for primary keys and the User.favoriteIds string array pattern to store favorite movie IDs without a separate join table — leveraging MongoDB's document model for efficient array push/pull operations via Prisma.
NextAuth.js integrates with Google and GitHub OAuth providers alongside credentials authentication. The Prisma adapter auto-manages Account and Session models for OAuth. JWT strategy allows stateless session verification on API routes without database lookups per request.
Core entities and how they relate to each other.
User account — supports credentials (email/bcrypt password) and OAuth sign-in (Google, GitHub). Stores favorite movie IDs as a MongoDB ObjectId array directly on the document
id (ObjectId, PK)nameemail (unique)emailVerified (DateTime)hashedPassword (optional for OAuth)imagefavoriteIds (String[] ObjectId array)createdAtupdatedAtOAuth provider links — stores Google/GitHub provider tokens and metadata. Created automatically by NextAuth.js Prisma Adapter on first OAuth sign-in
id (ObjectId, PK)userIdtypeproviderproviderAccountIdaccess_tokenrefresh_token@@unique([provider, providerAccountId])Stores active user sessions with unique tokens and expiry — managed by NextAuth.js Prisma Adapter
id (ObjectId, PK)sessionToken (unique)userIdexpiresEmail verification tokens with identifier and expiry — used by NextAuth.js for email verification flows
id (ObjectId, PK)identifiertoken (unique)expires@@unique([identifier, token])Movie content — stores all metadata and media URLs for the streaming catalog. Referenced by User.favoriteIds for the My List feature
id (ObjectId, PK)titledescriptionvideoUrlthumbnailUrlgenredurationStep-by-step walkthrough of the main user and system flows.
In a relational database, a favorites feature would require a separate UserFavorites join table. MongoDB doesn't have native join tables, and creating a separate collection for a simple many-to-many relationship adds unnecessary complexity.
Leveraged MongoDB's document model by storing favoriteIds as a String[] array directly on the User document. Adding a favorite uses Prisma's push operation, and removing uses lodash.without to filter the array. Fetching favorites uses Prisma's findMany with where: { id: { in: favoriteIds } } — no joins needed.
Toggling a favorite requires an API call, but waiting for the response before updating the UI creates a noticeable delay. The favorite icon flickers and the My List section lags behind the user's action.
After the API call returns, both SWR caches (useCurrentUser and useFavorites) are mutated directly with the response data. The currentUser cache gets the updated favoriteIds array, and the favorites list is re-fetched. This keeps the + / checkmark icon and the My List section in sync without a full page reload.
MongoDB doesn't have a native 'random document' query. Using findMany and shuffling in JavaScript loads all movies into memory, which doesn't scale.
Used a two-step approach: first get the total count via prismaDB.movie.count(), then generate a random index with Math.floor(Math.random() * count) and use Prisma's skip + take(1) to fetch a single random movie efficiently. This only loads one document regardless of collection size.
The Netflix-style navbar needs to be transparent when at the top of the page and transition to a dark background when the user scrolls down — without any flickering or jarring visual change.
Used a scroll event listener with a TOP_OFFSET threshold of 66px. When window.scrollY exceeds the threshold, a showBackground state toggles the bg-zinc-900 bg-opacity-90 classes with Tailwind's transition duration-500 for a smooth 500ms fade. The event listener is cleaned up on unmount to prevent memory leaks.
FlixNest demonstrates a cinematic streaming UI with Netflix-style interactions: auto-playing billboard hero, hover-expanding movie cards with CSS transforms, optimistic favorites toggling with SWR cache mutations, and a full-screen video player. The project showcases multi-provider authentication via NextAuth.js (credentials + Google + GitHub), MongoDB's document model for efficient favorites storage without join tables, and client-side data fetching patterns with SWR and Zustand.