https://github.com/ujjwalprajapati16/elib-api
elib-apis is a lightweight REST API built with Node.js, Express, and TypeScript. It supports user authentication (JWT), book CRUD, and file/image uploads via Cloudinary, backed by MongoDB.
https://github.com/ujjwalprajapati16/elib-api
cloudinary express jwt mongodb nodejs rest-api typescript
Last synced: about 2 months ago
JSON representation
elib-apis is a lightweight REST API built with Node.js, Express, and TypeScript. It supports user authentication (JWT), book CRUD, and file/image uploads via Cloudinary, backed by MongoDB.
- Host: GitHub
- URL: https://github.com/ujjwalprajapati16/elib-api
- Owner: Ujjwalprajapati16
- Created: 2025-09-14T13:52:31.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-12-02T10:20:56.000Z (7 months ago)
- Last Synced: 2025-12-05T05:42:04.885Z (7 months ago)
- Topics: cloudinary, express, jwt, mongodb, nodejs, rest-api, typescript
- Language: TypeScript
- Homepage:
- Size: 121 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# elib-apis
A modern, lightweight REST API backend for elib — a simple ebook library. It provides user authentication, book management with uploads, user ratings, insights/analytics, likes, and pagination.
- Runtime: Node.js (ESM), TypeScript
- Framework: Express
- Database: MongoDB (Mongoose)
- Uploads: Cloudinary (cover images and files)
- Auth: JWT (Bearer tokens)
## Features
- User registration and login with hashed passwords (bcrypt)
- JWT-based authentication middleware
- CRUD operations for books
- File uploads (cover image required; ebook file optional) via Multer + Cloudinary
- Book likes (toggle like/unlike)
- User ratings with comments
- Insights for authors (average rating, highest avg rated book, most recent rating)
- Pagination for listing books
- Centralized error handling
- CORS configured for a single frontend origin
## Database Schema
```mermaid
erDiagram
USER {
ObjectId _id PK
string name
string email
string password "hashed"
string role "user | author"
Date createdAt
Date updatedAt
}
BOOK {
ObjectId _id PK
string title
ObjectId authorFK "Ref: User"
string description
string genre
string coverImage "URL"
string file "URL"
number views
ObjectId[] likes "Array of User IDs"
DateWK createdAt
Date updatedAt
}
RATING {
ObjectId _id PK
ObjectId bookFK "Ref: Book"
ObjectId userFK "Ref: User"
number rating "1-5"
string comment
Date createdAt
Date updatedAt
}
%% Relationships
USER ||--o{ BOOK : "authors (1:N)"
USER ||--o{ RATING : "writes (1:N)"
BOOK ||--o{ RATING : "has (1:N)"
%% Many-to-Many relationship handled via array of IDs in Book model
BOOK }|..|{ USER : "liked by (M:N)"
```
## Data Flow Diagram (DFD)
```mermaid
graph TD
%% External Entities
Client[Client / User]
Cloudinary[Cloudinary Service]
MongoDB[(MongoDB Database)]
%% System Boundaries
subgraph "elib-api Application"
direction TB
%% Routers
UserRouter[User Router]
BookRouter[Book Router]
RateRouter[Rating Router]
InsightRouter[Insight Router]
%% Middleware
AuthMD[Auth Middleware]
UploadMD[Multer Upload]
%% Controllers
UserCtrl[User Controller]
BookCtrl[Book Controller]
RateCtrl[Rating Controller]
InsightCtrl[Insight Controller]
end
%% Data Flows
%% Auth Flow
Client -- "1. Register / Login" --> UserRouter
UserRouter --> UserCtrl
UserCtrl -- "Create/Find User" --> MongoDB
MongoDB -- "User Data" --> UserCtrl
UserCtrl -- "JWT Token" --> Client
%% Book Management Flow
Client -- "2. Upload Book (Multipart)" --> AuthMD
AuthMD -- "Validated User" --> UploadMD
UploadMD -- "File Stream" --> BookRouter
BookRouter --> BookCtrl
BookCtrl -- "Upload Cover/PDF" --> Cloudinary
Cloudinary -- "File URLs" --> BookCtrl
BookCtrl -- "Save Book Meta + URLs" --> MongoDB
MongoDB -- "Book Doc" --> BookCtrl
BookCtrl -- "Response" --> Client
%% Rating Flow
Client -- "3. Rate Book" --> AuthMD
AuthMD --> RateRouter
RateRouter --> RateCtrl
RateCtrl -- "Create Rating" --> MongoDB
%% Insight/Analytics Flow
Client -- "4. Request Analytics" --> InsightRouter
InsightRouter --> InsightCtrl
InsightCtrl -- "Aggregate Query" --> MongoDB
MongoDB -- "Aggregated Stats" --> InsightCtrl
InsightCtrl -- "JSON Report" --> Client
```
## Quick start
```bash
# 1) Install dependencies
npm install
# 2) Configure environment (see .env example below)
# Create a .env file in the project root
# 3) Start in dev (nodemon + ts-node)
npm run dev
# Server will run at http://localhost:3000 by default
```
## Environment variables
Create a .env in the project root with the following keys:
```bash
PORT=3000
NODE_ENV=development
MONGO_URI=mongodb://localhost:27017/elib
JWT_SECRET=replace-with-strong-secret
FRONTEND_URL=http://localhost:3001
# Cloudinary
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
```
Generate a strong JWT secret (PowerShell, no output to console):
```powershell
$bytes = New-Object byte[] 64; [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes); $env:JWT_SECRET = [Convert]::ToBase64String($bytes); Add-Content -Path .env -Value "JWT_SECRET=$env:JWT_SECRET"
```
## Project structure (key files)
- src/app.ts — Express app, routes, and middleware
- src/config/config.ts — environment configuration
- src/config/db.ts — Mongo connection helper
- src/middlewares/authenticate.ts — JWT auth middleware
- src/middlewares/globalErrorHandler.ts — error shape and handling
- src/controllers/*, src/models/*, src/routes/* — feature modules
- server.ts — app bootstrap
## API Reference
Base URL: http://localhost:3000
Error responses follow this shape:
```json
{
"status": "error",
"statusCode": 400,
"message": "Human readable error message"
}
```
Auth
- Authentication: Bearer
- Header: Authorization: Bearer
Health/Welcome
- GET /
- 200 OK
- Response:
```json
{ "message": "Welcome to the elib APIs" }
```
Users
1) Register
- Method: POST
- Path: /api/users/register
- Body (JSON):
```json
{
"name": "Alice",
"email": "alice@example.com",
"password": "password123",
"role": "author" // or "reader"
}
```
- 201 Created
- Response:
```json
{
"accessToken": "",
"user": { "name": "Alice", "email": "alice@example.com", "role": "author" }
}
```
2) Login
- Method: POST
- Path: /api/users/login
- Body (JSON):
```json
{
"email": "alice@example.com",
"password": "password123"
}
```
- 200 OK
- Response:
```json
{
"accessToken": "",
"user": { "name": "Alice", "email": "alice@example.com", "role": "author" }
}
```
Books
All endpoints under /api/books. Some actions require Authorization header.
Fields:
- title: string (required on create)
- genre: string (required on create)
- description: string (required on create)
- coverImage: file (required on create; form key: coverImage)
- file: file (optional; form key: file)
1) Create book
- Method: POST
- Path: /api/books/add
- Auth: Required (Bearer token)
- Content-Type: multipart/form-data
- Form fields:
- title: string
- genre: string
- description: string
- coverImage: file (required)
- file: file (optional)
- 201 Created
- Response:
```json
{
"message": "Book created successfully",
"book": {
"_id": "...",
"title": "...",
"genre": "...",
"description": "...",
"coverImage": "https://...",
"file": "https://..." | null,
"author": { "_id": "...", "name": "..." },
"createdAt": "...",
"updatedAt": "..."
}
}
```
2) Update book
- Method: PATCH
- Path: /api/books/update/:Bookid
- Auth: Required
- Content-Type: multipart/form-data
- Any of the following fields can be sent:
- title, genre, description, coverImage (file), file (file)
- 200 OK
- Response:
```json
{
"message": "Book updated successfully",
"book": { /* updated book */ }
}
```
3) List books with pagination
- Method: GET
- Path: /api/books/
- Query params:
- page: number (default 1)
- limit: number (default 10)
- 200 OK
- Response:
```json
{
"message": "Books listed successfully",
"books": [ /* books */ ],
"pagination": {
"totalBooks": 42,
"currentPage": 1,
"totalPages": 5,
"pageSize": 10
}
}
```
4) Book details (increments view count, returns ratings and average)
- Method: GET
- Path: /api/books/:Bookid
- 200 OK
- Response:
```json
{
"message": "Book details fetched successfully",
"book": { /* book incl. updated views */ },
"ratings": [
{ "_id": "...", "rating": 5, "comment": "...", "user": { "name": "..." } }
],
"averageRating": "4.50",
"totalRatings": 12
}
```
5) Delete book (also deletes associated ratings)
- Method: DELETE
- Path: /api/books/:Bookid
- Auth: Required (must be author)
- 200 OK
- Response:
```json
{ "message": "Book and related ratings deleted successfully" }
```
6) Toggle like/unlike a book
- Method: PATCH
- Path: /api/books/:Bookid/like
- Auth: Required
- 200 OK
- Response:
```json
{
"message": "Book liked successfully",
"likesCount": 3,
"book": { /* book */ }
}
```
or
```json
{
"message": "Book unliked successfully",
"likesCount": 2,
"book": { /* book */ }
}
```
Ratings
Base: /api/rate
1) Add rating to a book
- Method: POST
- Path: /api/rate/:BookId
- Auth: Required
- Body (JSON):
```json
{ "rating": 5, "comment": "Excellent read" }
```
- 200 OK
- Response:
```json
{ "message": "Rating added successfully" }
```
2) Delete a rating
- Method: DELETE
- Path: /api/rate/:BookId/:ratingId
- Auth: Required
- 200 OK
- Response:
```json
{ "message": "Rating deleted successfully" }
```
3) Get ratings for a book
- Method: GET
- Path: /api/rate/book/:BookId
- Auth: Required
- 200 OK
- Response:
```json
[ { "_id": "...", "rating": 5, "comment": "...", "book": "...", "user": "..." } ]
```
4) Get ratings for all books by an author
- Method: GET
- Path: /api/rate/author/:AuthorId
- Auth: Required
- 200 OK
- Response:
```json
{
"message": "All ratings for author's books fetched successfully",
"totalRatings": 10,
"ratings": [
{
"_id": "...",
"rating": 4,
"comment": "...",
"createdAt": "...",
"book": { "_id": "...", "title": "..." },
"reviewer": { "_id": "...", "name": "...", "email": "..." }
}
]
}
```
Insights (author analytics)
Base: /api/insight
1) Average rating for an author's books
- Method: GET
- Path: /api/insight/averageRating/:authorId
- 200 OK
- Response:
```json
{
"message": "Average rating fetched successfully",
"averageRating": 4.2,
"totalRatings": 57
}
```
2) Highest average rated book for an author
- Method: GET
- Path: /api/insight/heighestRatedBook/:authorId
- 200 OK
- Response:
```json
{
"message": "Highest average rated book fetched successfully",
"highestAvgRatedBook": "",
"averageRating": 4.8
}
```
3) Most recent rating for an author's books
- Method: GET
- Path: /api/insight/recentRating/:authorId
- 200 OK
- Response:
```json
{
"message": "Most recent rating fetched successfully",
"recentRating": {
"bookTitle": "...",
"rating": 5,
"comment": "...",
"createdAt": "...",
"reviewerName": "...",
"reviewerEmail": "..."
}
}
```
## Development notes
- ESM + TypeScript are configured with NodeNext in tsconfig.json and "type": "module" in package.json.
- JWT tokens include sub, email, and role, and expire in 7 days by default.
- Error handler returns a consistent JSON shape and includes stack traces only in development.
- CORS is restricted to FRONTEND_URL.
## Scripts
```json
{
"dev": "nodemon server.ts"
}
```
## License
ISC © Ujjwal Prajapati