- Overview
- Tech Stack
- Features
- Quick Start
- Environment Variables
- API Documentation
- Error Handling
- Rate Limiting
- File Upload
- Testing
- Contributing
A robust, scalable backend API for a YouTube-like video sharing platform built with Node.js, Express.js, and MongoDB. This API provides comprehensive functionality including user authentication, video management, social features, and real-time interactions.
- RESTful API Design - Clean, intuitive endpoints
- JWT Authentication - Secure token-based auth with refresh tokens
- File Upload & Cloud Storage - Cloudinary integration for media files
- Advanced Search & Filtering - Powerful video discovery features
- Social Features - Likes, comments, subscriptions, playlists
- Rate Limiting - Protection against abuse and spam
- Comprehensive Error Handling - Structured error responses
- Professional Documentation - Complete API reference
- Runtime: Node.js 18+
- Framework: Express.js
- Database: MongoDB with Mongoose ODM
- Authentication: JSON Web Tokens (JWT)
- File Storage: Cloudinary
- File Upload: Multer
- Security: bcrypt, express-rate-limit
- Validation: Custom middleware
- Environment: dotenv
- π User Authentication & Authorization
- π₯ Video Upload & Management
- π¬ Comments System
- π Likes & Reactions
- π Tweet-like Posts
- π Playlist Management
- π₯ User Subscriptions
- π Channel Analytics Dashboard
- π Advanced Search & Filtering
- π Pagination & Sorting
- π Rate Limiting & Security
- π± Responsive File Upload
- π― View Tracking
- π Real-time Updates
- π Performance Monitoring
- Node.js (v18 or higher)
- MongoDB (local or Atlas)
- Cloudinary account
- npm or yarn
-
Clone the repository
git clone <repository-url> cd youtube-clone-backend
-
Install dependencies
npm install
-
Set up environment variables
cp .env.sample .env
-
Configure your
.env
file (see Environment Variables) -
Start the server
# Development mode npm run dev # Production mode npm start
The server will start on http://localhost:8000
Create a .env
file in the root directory:
# Server Configuration
PORT=8000
NODE_ENV=development
# Database
MONGODB_URI=mongodb://localhost:27017/youtube-clone
# JWT Secrets
ACCESS_TOKEN_SECRET=your_access_token_secret
ACCESS_TOKEN_EXPIRY=1d
REFRESH_TOKEN_SECRET=your_refresh_token_secret
REFRESH_TOKEN_EXPIRY=7d
# Cloudinary Configuration
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# CORS
CORS_ORIGIN=http://localhost:3000
Base URL: http://localhost:8000/api/v1
All API responses follow this structure:
{
"statusCode": 200,
"data": {...},
"message": "Success message",
"success": true
}
POST /users/register
Body (multipart/form-data):
{
"username": "johndoe",
"email": "john@example.com",
"fullName": "John Doe",
"password": "password123",
"avatar": "file (optional, max 2MB)",
"coverImage": "file (optional, max 2MB)"
}
Response:
{
"statusCode": 201,
"data": {
"user": {
"_id": "user_id",
"username": "johndoe",
"email": "john@example.com",
"fullName": "John Doe",
"avatar": "cloudinary_url",
"coverImage": "cloudinary_url"
},
"accessToken": "jwt_token",
"refreshToken": "refresh_token"
},
"message": "User registered successfully"
}
POST /users/login
Body:
{
"email": "john@example.com",
"password": "password123"
}
Response:
{
"statusCode": 200,
"data": {
"user": {...},
"accessToken": "jwt_token",
"refreshToken": "refresh_token"
},
"message": "User logged in successfully"
}
DELETE /users/logout
Headers: Authorization: Bearer <access_token>
Response:
{
"statusCode": 200,
"data": {},
"message": "User logged out successfully"
}
POST /users/refresh-access-token
Body:
{
"refreshToken": "refresh_token"
}
GET /users/get-current-user
Headers: Authorization: Bearer <access_token>
PATCH /users/update-account-details
Headers: Authorization: Bearer <access_token>
Body:
{
"fullName": "Updated Name",
"email": "updated@example.com"
}
PATCH /users/update-user-avatar
Headers: Authorization: Bearer <access_token>
Body (multipart/form-data):
{
"avatar": "file (required, max 2MB, JPEG/PNG/GIF/WebP)"
}
PATCH /users/update-user-cover-image
Headers: Authorization: Bearer <access_token>
Body (multipart/form-data):
{
"coverImage": "file (required, max 2MB, JPEG/PNG/GIF/WebP)"
}
PATCH /users/change-current-password
Headers: Authorization: Bearer <access_token>
Body:
{
"oldPassword": "current_password",
"newPassword": "new_password"
}
GET /users/channel/:username
Parameters:
username
(string, required) - Username of the channel
Response:
{
"statusCode": 200,
"data": {
"channel": {
"_id": "user_id",
"username": "johndoe",
"fullName": "John Doe",
"avatar": "cloudinary_url",
"coverImage": "cloudinary_url",
"subscribersCount": 150,
"videosCount": 25,
"isSubscribed": false
}
},
"message": "Channel profile fetched successfully"
}
GET /users/get-user-watch-history
Headers: Authorization: Bearer <access_token>
Query Parameters:
page
(number, optional) - Page number (default: 1)limit
(number, optional) - Items per page (default: 10)
GET /videos
Query Parameters:
page
(number, optional) - Page number (default: 1)limit
(number, optional) - Videos per page (default: 10, max: 100)keyword
(string, optional) - Search in title/descriptionsortBy
(string, optional) - Sort by: 'createdAt', 'views', 'likesCount' (default: 'createdAt')sortType
(string, optional) - Sort order: 'asc', 'desc' (default: 'desc')userId
(string, optional) - Filter by user ID
Response:
{
"statusCode": 200,
"data": {
"videos": [
{
"_id": "video_id",
"title": "Video Title",
"description": "Video Description",
"videoFile": "cloudinary_url",
"thumbnail": "cloudinary_url",
"duration": 300,
"views": 1250,
"isPublished": true,
"owner": {
"_id": "user_id",
"username": "johndoe",
"fullName": "John Doe",
"avatar": "cloudinary_url"
},
"createdAt": "2024-01-01T00:00:00.000Z"
}
],
"pagination": {
"currentPage": 1,
"totalPages": 5,
"totalVideos": 50,
"hasNextPage": true,
"hasPrevPage": false
}
},
"message": "Videos fetched successfully"
}
POST /videos
Headers: Authorization: Bearer <access_token>
Body (multipart/form-data):
{
"title": "My Video Title",
"description": "Video description here",
"videoFile": "file (required, max 100MB, MP4/AVI/MKV/MOV/WMV)",
"thumbnail": "file (required, max 2MB, JPEG/PNG/GIF/WebP)"
}
Response:
{
"statusCode": 201,
"data": {
"_id": "video_id",
"title": "My Video Title",
"description": "Video description here",
"videoFile": "cloudinary_url",
"thumbnail": "cloudinary_url",
"duration": 0,
"views": 0,
"isPublished": false,
"owner": "user_id",
"createdAt": "2024-01-01T00:00:00.000Z"
},
"message": "Video uploaded successfully"
}
GET /videos/:videoId
Parameters:
videoId
(string, required) - Video ID
Response: Similar to video object above with like status and owner details
PATCH /videos/:videoId
Headers: Authorization: Bearer <access_token>
Body (multipart/form-data):
{
"title": "Updated Title",
"description": "Updated Description",
"thumbnail": "file (optional, max 2MB, JPEG/PNG/GIF/WebP)"
}
DELETE /videos/:videoId
Headers: Authorization: Bearer <access_token>
PATCH /videos/toggle/publish/:videoId
Headers: Authorization: Bearer <access_token>
GET /comments/:videoId
Parameters:
videoId
(string, required) - Video ID
Query Parameters:
page
(number, optional) - Page numberlimit
(number, optional) - Comments per page
POST /comments/:videoId
Headers: Authorization: Bearer <access_token>
Body:
{
"content": "Great video! Thanks for sharing."
}
PATCH /comments/id/:commentId
Headers: Authorization: Bearer <access_token>
Body:
{
"content": "Updated comment content"
}
DELETE /comments/id/:commentId
Headers: Authorization: Bearer <access_token>
POST /likes/toggle/video/:videoId
Headers: Authorization: Bearer <access_token>
POST /likes/toggle/comment/:commentId
Headers: Authorization: Bearer <access_token>
POST /likes/toggle/tweet/:tweetId
Headers: Authorization: Bearer <access_token>
GET /likes/videos
Headers: Authorization: Bearer <access_token>
POST /playlists
Headers: Authorization: Bearer <access_token>
Body:
{
"name": "My Playlist",
"description": "Playlist description",
"isPublic": false
}
GET /playlists/:playlistId
Headers: Authorization: Bearer <access_token>
PATCH /playlists/:playlistId
Headers: Authorization: Bearer <access_token>
DELETE /playlists/:playlistId
Headers: Authorization: Bearer <access_token>
PATCH /playlists/add/:videoId/:playlistId
Headers: Authorization: Bearer <access_token>
PATCH /playlists/remove/:videoId/:playlistId
Headers: Authorization: Bearer <access_token>
GET /playlists/user/:userId
Headers: Authorization: Bearer <access_token>
POST /tweets
Headers: Authorization: Bearer <access_token>
Body:
{
"content": "This is my tweet content (max 280 characters)"
}
GET /tweets/user/:userId
PATCH /tweets/:tweetId
Headers: Authorization: Bearer <access_token>
DELETE /tweets/:tweetId
Headers: Authorization: Bearer <access_token>
GET /subscriptions
Headers: Authorization: Bearer <access_token>
GET /subscriptions/channel/:channelId
POST /subscriptions/channel/:channelId
Headers: Authorization: Bearer <access_token>
GET /subscriptions/user/:subscriberId
Headers: Authorization: Bearer <access_token>
GET /dashboard/stats
Headers: Authorization: Bearer <access_token>
Response:
{
"statusCode": 200,
"data": {
"totalVideos": 25,
"totalViews": 10500,
"totalSubscribers": 150,
"totalLikes": 850,
"totalComments": 320
},
"message": "Channel statistics fetched successfully"
}
GET /dashboard/videos
Headers: Authorization: Bearer <access_token>
Query Parameters:
page
(number, optional) - Page numberlimit
(number, optional) - Videos per pagesortBy
(string, optional) - Sort fieldsortType
(string, optional) - Sort order
GET /healthcheck
Response:
{
"statusCode": 200,
"data": {
"status": "OK",
"timestamp": "2024-01-01T00:00:00.000Z",
"uptime": 3600,
"environment": "development"
},
"message": "Service is healthy"
}
All errors follow this structure:
{
"statusCode": 400,
"data": null,
"message": "Error description",
"success": false,
"errors": [] // Additional error details if any
}
400
- Bad Request (validation errors)401
- Unauthorized (authentication required)403
- Forbidden (insufficient permissions)404
- Not Found (resource doesn't exist)409
- Conflict (duplicate resource)413
- Payload Too Large (file size exceeded)429
- Too Many Requests (rate limit exceeded)500
- Internal Server Error
API endpoints are protected with rate limiting:
- General routes: 100 requests per 15 minutes per IP
- Auth routes: 5 requests per 15 minutes per IP
- Upload routes: 10 requests per 15 minutes per IP
- Search routes: 50 requests per 15 minutes per IP
Rate limit headers are included in responses:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1640995200
Images (avatars, thumbnails, covers):
- Formats: JPEG, PNG, GIF, WebP
- Max size: 2MB
Videos:
- Formats: MP4, AVI, MKV, MOV, WMV
- Max size: 100MB
- Files are temporarily stored on server
- Uploaded to Cloudinary cloud storage
- Temporary files are automatically cleaned up
- Database is updated with cloud URLs
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Run tests in watch mode
npm run test:watch
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For support, email sayankb.001@gmail.com or create an issue on GitHub.
Built with β€οΈ using Node.js, Express.js, and MongoDB