A production-grade YouTube-like video platform with complex social graph queries, JWT refresh-token rotation, and Cloudinary-backed media storage — built with a clean MVC backend architecture.
┌──────────────────────────────────────────────────────────────────┐
│ CLIENT: React + Tailwind │
│ Video Player · Feed · Subscriptions · Playlists │
└───────────────────────────┬──────────────────────────────────────┘
│ REST API (JSON)
┌───────────────────────────▼──────────────────────────────────────┐
│ EXPRESS.JS API SERVER │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ Auth │ │ Middleware │ │ Route Controllers │ │
│ │ Layer │ │ Layer │ │ │ │
│ │ │ │ │ │ • /videos │ │
│ │ JWT access │ │ • Auth guard│ │ • /users │ │
│ │ + refresh │ │ • Error │ │ • /subscriptions │ │
│ │ rotation │ │ handler │ │ • /playlists │ │
│ │ │ │ • Request │ │ • /likes │ │
│ │ Refresh │ │ validator │ │ • /watch-history │ │
│ │ token │ │ • Multer │ │ • /comments │ │
│ │ invalidated│ │ (uploads) │ │ │ │
│ │ on rotate │ │ │ │ Aggregation pipelines │ │
│ └─────────────┘ └──────────────┘ │ for social graph queries│ │
│ └──────────────────────────┘ │
└──────────┬───────────────────────────────────────┬───────────────┘
│ │
┌──────────▼──────────┐ ┌──────────▼──────────────┐
│ MongoDB │ │ Cloudinary │
│ │ │ │
│ • Users │ │ • Video storage & CDN │
│ • Videos │ │ • Thumbnail storage │
│ • Subscriptions │ │ • Avatar/cover images │
│ • Playlists │ │ • Streaming URLs │
│ • Likes │ └─────────────────────────┘
│ • Watch History │
│ • Comments │
│ │
│ Aggregation pipes │
│ for N+1 avoidance │
└─────────────────────┘
| Feature | Technical Detail |
|---|---|
| JWT Auth | Access + refresh token rotation; refresh tokens invalidated on every rotate |
| Social graph queries | Subscriptions, likes, watch history, playlists via MongoDB aggregation — avoids N+1 |
| Paginated feeds | Single aggregation stage for paginated video feed (no multiple round-trips) |
| Media storage | Cloudinary-backed video + thumbnail + avatar storage with CDN delivery |
| Role-based access | Middleware-enforced route guards (owner-only edit/delete) |
| Modular MVC | Clean separation: routes → controllers → services → models |
| Reusable middleware | Auth, error handling, request validation as composable Express middleware |
| Layer | Technology |
|---|---|
| Runtime | Node.js 18+ |
| Framework | Express.js 4 |
| Frontend | React 18 + Tailwind CSS 3 |
| Database | MongoDB 6 (Mongoose ODM) |
| Media CDN | Cloudinary |
| Auth | JWT (access token + refresh token rotation) |
| File handling | Multer (local buffer) → Cloudinary upload |
- Node.js 18+
- MongoDB 6+ (local or Atlas)
- Cloudinary account (free tier works)
git clone https://github.com/bajpaisatvic/Video-Library-backend.git
cd Video-Library-backend
npm installcp .env.example .envEdit .env:
PORT=8000
MONGODB_URI=mongodb://localhost:27017/vlib
CORS_ORIGIN=http://localhost:5173
ACCESS_TOKEN_SECRET=your_access_secret
ACCESS_TOKEN_EXPIRY=15m
REFRESH_TOKEN_SECRET=your_refresh_secret
REFRESH_TOKEN_EXPIRY=7d
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret# Development (with hot reload)
npm run dev
# Production
npm startServer runs at http://localhost:8000
Video-Library-backend/
├── src/
│ ├── controllers/ # Route handlers — thin, delegate to services
│ │ ├── user.controller.js
│ │ ├── video.controller.js
│ │ ├── subscription.controller.js
│ │ ├── playlist.controller.js
│ │ ├── like.controller.js
│ │ └── comment.controller.js
│ ├── models/ # Mongoose schemas
│ │ ├── user.model.js
│ │ ├── video.model.js
│ │ ├── subscription.model.js
│ │ └── ...
│ ├── routes/ # Express routers
│ ├── middlewares/ # Auth guard, error handler, multer, validator
│ ├── utils/ # Cloudinary helper, ApiError, ApiResponse wrappers
│ └── db/ # MongoDB connection
├── public/ # Temp upload directory (cleared after Cloudinary upload)
├── .env.example
└── package.json
MongoDB's aggregation pipeline handles complex social graph lookups in a single query rather than multiple round-trips:
// Example: Paginated feed with subscription check + like count in one pipeline
Video.aggregate([
{ $match: { isPublished: true } },
{ $lookup: { from: 'subscriptions', ... } }, // subscription status
{ $lookup: { from: 'likes', ... } }, // like count
{ $addFields: { isSubscribed: ..., likeCount: ... } },
{ $project: { ... } },
{ $skip: (page - 1) * limit },
{ $limit: limit }
])On every token refresh, the old refresh token is invalidated in the DB. This means stolen refresh tokens cannot be reused after a legitimate refresh has occurred.
Login → [access_token (15m), refresh_token (7d)] stored
↓
Access token expires → POST /api/auth/refresh
↓
Server: validate old refresh_token → issue new pair → invalidate old refresh_token in DB
↓
Attacker's copy of old refresh_token → rejected
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/v1/auth/register |
POST | — | Register with avatar upload |
/api/v1/auth/login |
POST | — | Returns access + refresh tokens |
/api/v1/auth/refresh |
POST | Refresh token | Rotates tokens |
/api/v1/videos |
GET | — | Paginated video feed |
/api/v1/videos/:id |
GET | — | Video details + stream URL |
/api/v1/videos |
POST | ✅ | Upload video + thumbnail |
/api/v1/videos/:id |
PATCH/DELETE | ✅ Owner | Edit or delete video |
/api/v1/subscriptions/:channelId |
POST | ✅ | Subscribe/unsubscribe toggle |
/api/v1/likes/video/:videoId |
POST | ✅ | Like/unlike toggle |
/api/v1/playlists |
GET/POST | ✅ | Create and fetch playlists |
/api/v1/users/watch-history |
GET | ✅ | Get watch history |
- Passwords hashed with bcrypt before storage
- Refresh tokens stored hashed in DB (not plain)
- Tokens rotated on every refresh — stolen tokens expire immediately after legitimate use
- Multer configured with file type and size limits
- CORS restricted to allowed origins via env config
PRs welcome. Please open an issue for major changes first.
MIT License — see LICENSE for details.
Built with ❤️ by Satvic Bajpai