A full-stack REST API for managing digital books with user authentication, file uploads, and CRUD operations. Built with Node.js, Express, TypeScript, MongoDB, and Cloudinary.
- β User Authentication - JWT-based registration and login
- β Book Management - Complete CRUD operations for books
- β File Upload - Cover images and PDF files via Cloudinary
- β Secure Access - Protected routes with authentication middleware
- β TypeScript - Full type safety throughout the application
- β MongoDB - NoSQL database with Mongoose ODM
- β Cloud Storage - Cloudinary integration for file management
- Technologies Used
- Prerequisites
- Installation
- Environment Variables
- API Endpoints
- Project Structure
- Usage Examples
- Contributing
- License
- Backend Framework: Node.js, Express.js
- Language: TypeScript
- Database: MongoDB with Mongoose
- Authentication: JWT (JSON Web Tokens)
- File Upload: Multer
- Cloud Storage: Cloudinary
- Password Hashing: bcrypt
- Validation: express-validator
- Error Handling: http-errors
Before running this project, make sure you have:
- Node.js (v18 or higher)
- MongoDB (local or Atlas)
- Cloudinary account
- npm or yarn package manager
- Clone the repository
git clone https://github.com/Shreeja5714/elib.git
cd elib- Install dependencies
npm install- Create environment file
cp .env.example .env-
Configure environment variables (see below)
-
Start MongoDB (if running locally)
mongod- Run the development server
npm run devThe server will start at http://localhost:5513
Create a .env file in the root directory:
# Server Configuration
PORT=5513
NODE_ENV=development
# Database
DATABASE_URL=mongodb://localhost:27017/elib
# JWT Secret
JWT_SECRET=your_super_secret_jwt_key_here
# Cloudinary Configuration
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret- Sign up at cloudinary.com
- Go to Dashboard
- Copy Cloud Name, API Key, and API Secret
POST /api/users/register
Content-Type: application/json
{
"name": "Shreeja Vyas",
"email": "shreeja@example.com",
"password": "password123"
}Response:
{
"message": "User registered successfully",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "507f1f77bcf86cd799439011",
"name": "Shreeja Vyas",
"email": "shreeja@example.com"
}
}POST /api/users/login
Content-Type: application/json
{
"email": "shreeja@example.com",
"password": "password123"
}Response:
{
"message": "Login successful",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "507f1f77bcf86cd799439011",
"name": "Shreeja Vyas",
"email": "shreeja@example.com"
}
}Note: All book routes require authentication. Include the JWT token in the Authorization header:
Authorization: Bearer <your_token_here>
POST /api/books
Authorization: Bearer <token>
Content-Type: multipart/form-data
Fields:
- title: "Book Title"
- genre: "Fiction"
- coverImage: <image file>
- file: <pdf file>Response:
{
"message": "Book created successfully",
"data": {
"id": "507f1f77bcf86cd799439011",
"title": "Book Title",
"coverImage": "https://res.cloudinary.com/...",
"file": "https://res.cloudinary.com/..."
}
}GET /api/booksResponse:
{
"message": "Books retrieved successfully",
"data": [
{
"_id": "507f1f77bcf86cd799439011",
"title": "Book Title",
"genre": "Fiction",
"author": {
"_id": "507f1f77bcf86cd799439012",
"name": "John Doe"
},
"coverImage": "https://res.cloudinary.com/...",
"file": "https://res.cloudinary.com/...",
"createdAt": "2025-01-20T10:30:00.000Z",
"updatedAt": "2025-01-20T10:30:00.000Z"
}
]
}GET /api/books/:bookIdResponse:
{
"message": "Book retrieved successfully",
"data": {
"_id": "507f1f77bcf86cd799439011",
"title": "Book Title",
"genre": "Fiction",
"author": {
"_id": "507f1f77bcf86cd799439012",
"name": "John Doe",
"email": "john@example.com"
},
"coverImage": "https://res.cloudinary.com/...",
"file": "https://res.cloudinary.com/...",
"createdAt": "2025-01-20T10:30:00.000Z"
}
}PATCH /api/books/:bookId
Authorization: Bearer <token>
Content-Type: multipart/form-data
Fields (all optional):
- title: "Updated Title"
- genre: "Updated Genre"
- coverImage: <new image file>
- file: <new pdf file>Response:
{
"message": "Book updated successfully",
"data": {
"_id": "507f1f77bcf86cd799439011",
"title": "Updated Title",
"genre": "Updated Genre",
"coverImage": "https://res.cloudinary.com/...",
"file": "https://res.cloudinary.com/..."
}
}DELETE /api/books/:bookId
Authorization: Bearer <token>Response:
{
"message": "Book deleted successfully"
}elib/
βββ src/
β βββ config/
β β βββ config.ts # Environment configuration
β β βββ db.ts # Database connection
β β βββ cloudinary.ts # Cloudinary setup
β βββ middleware/
β β βββ authenticate.ts # JWT authentication middleware
β β βββ multer.ts # File upload configuration
β β βββ globalErrorHandler.ts
β βββ user/
β β βββ userModel.ts # User schema
β β βββ userController.ts # User controllers
β β βββ userRouter.ts # User routes
β βββ book/
β β βββ bookModel.ts # Book schema
β β βββ bookController.ts # Book controllers (CRUD)
β β βββ bookRouter.ts # Book routes
β βββ app.ts # Express app setup
β βββ server.ts # Server entry point
βββ public/
β βββ data/
β βββ uploads/ # Temporary file storage
βββ .env # Environment variables
βββ .env.example # Environment template
βββ .gitignore
βββ package.json
βββ tsconfig.json
βββ README.md
Register a user:
curl -X POST http://localhost:5513/api/users/register \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"password": "password123"
}'Create a book:
curl -X POST http://localhost:5513/api/books \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-F "title=My Book" \
-F "genre=Fiction" \
-F "coverImage=@/path/to/cover.jpg" \
-F "file=@/path/to/book.pdf"- Register/Login to get your JWT token
- Set Authorization header:
Bearer <your_token> - For file uploads, use
form-databody type - Add fields: title, genre, coverImage (file), file (file)
- β Password hashing with bcrypt
- β JWT token authentication
- β Protected routes
- β User authorization checks
- β Input validation
- β Secure file handling
- β Environment variable protection
The API uses standard HTTP status codes:
200- Success201- Created204- Deleted400- Bad Request401- Unauthorized403- Forbidden404- Not Found409- Conflict500- Internal Server Error
Error Response Format:
{
"message": "Error description",
"errorStack": "Stack trace (in development only)"
}Use Postman, Thunder Client, or cURL to test endpoints.
npm test# Development
npm run dev # Start development server with nodemon
# Production
npm run build # Compile TypeScript to JavaScript
npm start # Start production server
# Type checking
npm run type-check # Check TypeScript typesContributions are welcome! Please follow these steps:
- 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
- Use TypeScript
- Follow existing code patterns
- Add comments for complex logic
- Update README if needed
Shreeja Vyas
- GitHub: @Shreeja5714
- Email: shreeja228@gmail.com
- Express.js documentation
- MongoDB documentation
- Cloudinary API
- Coder's Gyan Youtube Video
Made with β€οΈ by Shreeja Vyas