Node.js + Express + MongoDB API for user authentication and book management with owner/admin permissions.
- Create users with secure passwords.
- Authenticate users with JWT.
- Provide public read access to books.
- Restrict create/update/delete actions with role and ownership checks.
- Return clean JSON responses only.
- Node.js
- Express
- MongoDB
- Mongoose
- bcryptjs
- jsonwebtoken
- express-validator
- dotenv
mon-premier-serveur/
.env
.gitignore
package.json
package-lock.json
README.md
server.js
src/
config/
db.js
controllers/
authController.js
bookController.js
middlewares/
authMiddleware.js
errorMiddleware.js
models/
User.js
Book.js
routes/
authRoutes.js
bookRoutes.js
validators/
authValidators.js
bookValidators.js
npm installCreate a .env file at the project root:
PORT=3050
MONGODB_URI=mongodb://127.0.0.1:27017/leclerc_library
JWT_SECRET=your_long_random_secret
JWT_EXPIRES_IN=1dDevelopment mode:
npm run devProduction mode:
npm startExpected startup logs:
MongoDB connected
Server started on http://localhost:3050
Note:
dotenvis configured withquiet: trueinserver.jsto avoid noisy startup messages.
Standard success response example:
{
"success": true,
"message": "Book created successfully",
"data": {
"book": {
"_id": "BOOK_ID",
"title": "Clean Code"
}
}
}Standard error response example:
{
"success": false,
"message": "Server error"
}Validation error response example:
{
"success": false,
"message": "Validation failed",
"data": [
{
"type": "field",
"value": "bad-email",
"msg": "Please provide a valid email address",
"path": "email",
"location": "body"
}
]
}200request successful201resource created400bad request or validation error401unauthenticated or invalid token403forbidden action404resource or route not found409conflict (duplicate unique field)500internal server error
Base URL:
http://localhost:3050
GET /
Expected success response (200):
{
"success": true,
"message": "Server is running"
}POST /api/auth/register
Body:
{
"name": "Test User",
"email": "test@example.com",
"password": "test12345"
}Expected success response (201):
{
"success": true,
"message": "User created successfully",
"data": {
"token": "JWT_TOKEN",
"user": {
"id": "USER_ID",
"name": "Test User",
"email": "test@example.com",
"role": "user"
}
}
}Expected error responses:
400validation error
{
"success": false,
"message": "Validation failed",
"data": []
}409duplicate email
{
"success": false,
"message": "Unable to create account"
}500server error
{
"success": false,
"message": "Server error"
}POST /api/auth/login
Body:
{
"email": "test@example.com",
"password": "test12345"
}Expected success response (200):
{
"success": true,
"message": "Login successful",
"data": {
"token": "JWT_TOKEN",
"user": {
"id": "USER_ID",
"name": "Test User",
"email": "test@example.com",
"role": "user"
}
}
}Expected error responses:
400validation error
{
"success": false,
"message": "Validation failed",
"data": []
}401invalid credentials
{
"success": false,
"message": "Invalid credentials"
}500server error
{
"success": false,
"message": "Server error"
}GET /api/auth/me
Headers:
Authorization: Bearer JWT_TOKEN
Expected success response (200):
{
"success": true,
"message": "Current user",
"data": {
"user": {
"id": "USER_ID",
"name": "Test User",
"email": "test@example.com",
"role": "user"
}
}
}Expected error responses:
401missing token
{
"success": false,
"message": "Unauthorized access"
}401invalid token
{
"success": false,
"message": "Invalid or expired token"
}404user not found
{
"success": false,
"message": "User not found"
}GET /api/books
Expected success response (200):
{
"success": true,
"message": "Books fetched successfully",
"data": {
"books": []
}
}Expected error response:
500
{
"success": false,
"message": "Server error"
}GET /api/books/:id
Expected success response (200):
{
"success": true,
"message": "Book fetched successfully",
"data": {
"book": {
"_id": "BOOK_ID",
"title": "Clean Code",
"author": "Robert C. Martin",
"isbn": "9780132350884",
"description": "Software craftsmanship basics.",
"owner": {
"_id": "USER_ID",
"name": "Test User",
"email": "test@example.com",
"role": "user"
},
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"__v": 0
}
}
}Expected error responses:
400invalid id
{
"success": false,
"message": "Invalid identifier"
}404not found
{
"success": false,
"message": "Book not found"
}POST /api/books
Headers:
Authorization: Bearer JWT_TOKEN
Body:
{
"title": "Clean Code",
"author": "Robert C. Martin",
"isbn": "9780132350884",
"description": "Software craftsmanship basics."
}Expected success response (201):
{
"success": true,
"message": "Book created successfully",
"data": {
"book": {
"_id": "BOOK_ID",
"title": "Clean Code",
"author": "Robert C. Martin",
"isbn": "9780132350884",
"description": "Software craftsmanship basics.",
"owner": "USER_ID",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"__v": 0
}
}
}Expected error responses:
400validation error
{
"success": false,
"message": "Validation failed",
"data": []
}401missing token
{
"success": false,
"message": "Unauthorized access"
}409duplicate ISBN
{
"success": false,
"message": "ISBN already in use"
}PUT /api/books/:id
Headers:
Authorization: Bearer JWT_TOKEN
Body:
{
"title": "Clean Code",
"author": "Robert C. Martin",
"isbn": "9780132350884",
"description": "Updated description"
}Expected success response (200):
{
"success": true,
"message": "Book updated successfully",
"data": {
"book": {}
}
}Expected error responses:
400invalid id or validation failure
{
"success": false,
"message": "Invalid identifier"
}or
{
"success": false,
"message": "Validation failed",
"data": []
}401missing or invalid token
{
"success": false,
"message": "Unauthorized access"
}or
{
"success": false,
"message": "Invalid or expired token"
}403not owner and not admin
{
"success": false,
"message": "Action forbidden"
}404book not found
{
"success": false,
"message": "Book not found"
}409duplicate ISBN
{
"success": false,
"message": "ISBN already in use"
}DELETE /api/books/:id
Headers:
Authorization: Bearer JWT_TOKEN
Expected success response (200):
{
"success": true,
"message": "Book deleted successfully",
"data": {
"book": {}
}
}Expected error responses:
400invalid id
{
"success": false,
"message": "Invalid identifier"
}401missing or invalid token
{
"success": false,
"message": "Unauthorized access"
}or
{
"success": false,
"message": "Invalid or expired token"
}403not owner and not admin
{
"success": false,
"message": "Action forbidden"
}404book not found
{
"success": false,
"message": "Book not found"
}GET /api/unknown
Expected error response (404):
{
"success": false,
"message": "Route not found"
}- Register a user.
- Login and copy the token.
- Call
GET /api/auth/mewithBearertoken. - Create a book with token.
- Update and delete the book with the same user.
- Create a second user and verify
403on update/delete.
Delete the whole database:
node -e "require('dotenv').config({ path: '.env', quiet: true }); const mongoose = require('mongoose'); (async () => { await mongoose.connect(process.env.MONGODB_URI); await mongoose.connection.db.dropDatabase(); console.log('Database cleared'); await mongoose.disconnect(); })().catch(err => { console.error(err); process.exit(1); });"Delete only users and books collections:
node -e "require('dotenv').config({ path: '.env', quiet: true }); const mongoose = require('mongoose'); (async () => { await mongoose.connect(process.env.MONGODB_URI); await mongoose.connection.collection('users').deleteMany({}); await mongoose.connection.collection('books').deleteMany({}); console.log('Users and books collections cleared'); await mongoose.disconnect(); })().catch(err => { console.error(err); process.exit(1); });"- Password is hashed with bcrypt.
- Password is never returned in API responses.
- Protected routes require
Authorization: Bearer <token>. ownerstores the user id that created the book.- Admin can perform delete operations even when not owner.