Skip to content

bajpaisatvic/Video-Library-backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

📺 VLIB — Full-Stack Video Streaming Platform

Node.js Express React MongoDB Cloudinary JWT Tailwind CSS

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.


🏗️ Architecture Overview

┌──────────────────────────────────────────────────────────────────┐
│                     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  │
└─────────────────────┘

✨ Key Features

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

🛠️ Tech Stack

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

🚀 Getting Started

Prerequisites

  • Node.js 18+
  • MongoDB 6+ (local or Atlas)
  • Cloudinary account (free tier works)

1. Clone and install

git clone https://github.com/bajpaisatvic/Video-Library-backend.git
cd Video-Library-backend
npm install

2. Configure environment variables

cp .env.example .env

Edit .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

3. Start the server

# Development (with hot reload)
npm run dev

# Production
npm start

Server runs at http://localhost:8000


📁 Project Structure

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

⚙️ Key Design Decisions

Avoiding N+1 in Social Graph Queries

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 }
])

JWT Refresh Token Rotation

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

📊 API Reference

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

🔒 Security

  • 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

🤝 Contributing

PRs welcome. Please open an issue for major changes first.


📄 License

MIT License — see LICENSE for details.


Built with ❤️ by Satvic Bajpai

About

Full-stack YouTube-like video platform — JWT refresh-token rotation, MongoDB aggregation pipelines, Cloudinary CDN

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors