DEV Community

Ameh Mathias Ejeh
Ameh Mathias Ejeh

Posted on

School Library System API

A RESTful API for managing a school library — authors, books, students, library attendants, and book borrowing/returns. Built with Node.js (ESM), Express.js, and MongoDB/Mongoose.


Table of Contents


Features

  • Full CRUD for Authors, Books, Students, and Library Attendants
  • Book borrowing and returning with full relationship tracking
  • Auto-population of authors, student, and attendant when a book is OUT
  • Overdue detection (isOverdue flag on responses)
  • Pagination on all list endpoints
  • Search by title or author name
  • Duplicate ISBN prevention (unique index + 409 error)
  • Request validation with express-validator
  • JWT authentication (attendants log in and receive a token)
  • Global error handler with meaningful messages
  • ESM imports throughout (no CommonJS)

Tech Stack

Layer Technology
Runtime Node.js (ESM)
Framework Express.js
Database MongoDB via Mongoose
Auth JSON Web Tokens (jsonwebtoken)
Passwords bcryptjs
Validation express-validator
Config dotenv

Project Structure

library-system/
├── config/
│   └── db.js                  # MongoDB connection
├── controllers/
│   ├── authorController.js
│   ├── bookController.js
│   ├── studentController.js
│   └── attendantController.js
├── middleware/
│   ├── auth.js                # JWT protect middleware
│   ├── errorHandler.js        # Global error handler
│   └── validators.js          # express-validator rules
├── models/
│   ├── Author.js
│   ├── Book.js
│   ├── Student.js
│   └── LibraryAttendant.js
├── routes/
│   ├── authorRoutes.js
│   ├── bookRoutes.js
│   ├── studentRoutes.js
│   └── attendantRoutes.js
├── .env.example
├── package.json
├── app.js
└── README.md
Enter fullscreen mode Exit fullscreen mode

Setup & Installation

Prerequisites

  • Node.js
  • MongoDB running locally or a MongoDB Atlas URI

Steps

# 1. Clone the repository
git clone <repo-url>
cd school-library-system

# 2. Install dependencies
npm install

# 3. Configure environment
cp .env.example .env
# Edit .env with your values

# 4. Start the server
npm start          # production
npm run dev        # development (auto-restarts on file changes)
Enter fullscreen mode Exit fullscreen mode

The API will be available at: http://localhost:5000

Health check: GET http://localhost:5000/health


Environment Variables

PORT=5000
MONGO_URI=mongodb://localhost:27017/library-system
JWT_SECRET=your_super_secret_jwt_key_change_in_production
JWT_EXPIRES_IN=7d
Enter fullscreen mode Exit fullscreen mode
Variable Description Default
PORT Port the server listens on 5000
MONGO_URI MongoDB connection string mongodb://localhost:27017/library-system
JWT_SECRET Secret for signing JWT tokens (required)
JWT_EXPIRES_IN Token expiry duration 7d

API Documentation

Base URL: http://localhost:5000/api/v1

Response Format

All responses follow a consistent envelope:

{
  "success": true,
  "data": { ... },
  "pagination": {
    "total": 42,
    "page": 1,
    "limit": 10,
    "pages": 5
  }
}
Enter fullscreen mode Exit fullscreen mode

Errors:

{
  "success": false,
  "message": "Human-readable error message",
  "errors": [{ "field": "email", "message": "Invalid email" }]
}
Enter fullscreen mode Exit fullscreen mode

Pagination & Filtering (all GET list endpoints)

Query Param Type Description
page Number Page number (default: 1)
limit Number Results per page (default: 10)
search String Search term (varies by resource)

Authentication

JWT is required for all write operations and sensitive reads. Login as an attendant to get a token.

Protected routes require the header:

Authorization: Bearer <token>
Enter fullscreen mode Exit fullscreen mode

POST /attendants/login

Login and receive a JWT token.

Request Body:

{
  "email": "ameh@library.edu",
  "password": "secret123"
}
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "data": {
    "_id": "664abc...",
    "name": "Ameh Mathias",
    "staffId": "STF001",
    "email": "ameh@library.edu"
  }
}
Enter fullscreen mode Exit fullscreen mode

Authors

Method Endpoint Auth Description
GET /authors No List all authors
GET /authors/:id No Get single author
POST /authors Yes Create author
PUT /authors/:id Yes Update author
DELETE /authors/:id Yes Delete author

GET /authors

Query params: page, limit, search (matches name).

Response:

{
  "success": true,
  "data": [
    {
      "_id": "664abc123...",
      "name": "Chinua Achebe",
      "bio": "Nigerian novelist and poet.",
      "createdAt": "2024-06-01T10:00:00.000Z"
    }
  ],
  "pagination": { "total": 1, "page": 1, "limit": 10, "pages": 1 }
}
Enter fullscreen mode Exit fullscreen mode

POST /authors 🔒

{
  "name": "Chinua Achebe",
  "bio": "Nigerian novelist and poet."
}
Enter fullscreen mode Exit fullscreen mode

PUT /authors/:id 🔒

{
  "bio": "Updated biography text."
}
Enter fullscreen mode Exit fullscreen mode

Books

Method Endpoint Auth Description
GET /books No List books (paginated)
GET /books/:id No Get single book
POST /books Yes Create book
PUT /books/:id Yes Update book
DELETE /books/:id Yes Delete book (must be IN)
POST /books/:id/borrow Yes Borrow a book
POST /books/:id/return Yes Return a book

GET /books

Query params: page, limit, search (title), author (author name), status (IN or OUT).

Response when status = OUT:

{
  "success": true,
  "data": [{
    "_id": "664abc...",
    "title": "Things Fall Apart",
    "isbn": "978-0-385-47454-2",
    "status": "OUT",
    "isOverdue": false,
    "returnDate": "2024-07-01T00:00:00.000Z",
    "authors": [{ "_id": "...", "name": "Chinua Achebe", "bio": "..." }],
    "borrowedBy": { "_id": "...", "name": "Emeka Obi", "studentId": "STU001" },
    "issuedBy": { "_id": "...", "name": "Jane Doe", "staffId": "STF001" }
  }]
}
Enter fullscreen mode Exit fullscreen mode

POST /books 🔒

{
  "title": "Things Fall Apart",
  "isbn": "978-0-385-47454-2",
  "authors": ["664abc123..."]
}
Enter fullscreen mode Exit fullscreen mode

POST /books/:id/borrow 🔒

Book must have status: "IN".

{
  "studentId": "664def456...",
  "attendantId": "664ghi789...",
  "returnDate": "2024-07-15T00:00:00.000Z"
}
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "success": true,
  "message": "Book borrowed successfully.",
  "data": {
    "_id": "...",
    "title": "Things Fall Apart",
    "status": "OUT",
    "returnDate": "2024-07-15T00:00:00.000Z",
    "borrowedBy": { "name": "Emeka Obi", "studentId": "STU001" },
    "issuedBy": { "name": "Jane Doe", "staffId": "STF001" }
  }
}
Enter fullscreen mode Exit fullscreen mode

POST /books/:id/return 🔒

Book must have status: "OUT". No request body needed.

Response:

{
  "success": true,
  "message": "Book returned successfully.",
  "data": {
    "_id": "...",
    "title": "Things Fall Apart",
    "status": "IN",
    "borrowedBy": null,
    "issuedBy": null,
    "returnDate": null
  }
}
Enter fullscreen mode Exit fullscreen mode

Students

Method Endpoint Auth Description
GET /students Yes List all students
GET /students/:id Yes Get single student (includes borrowed books)
POST /students Yes Create student
PUT /students/:id Yes Update student
DELETE /students/:id Yes Delete student (cannot have active loans)

POST /students 🔒

{
  "name": "Emeka Obi",
  "email": "emeka@school.edu",
  "studentId": "STU001"
}
Enter fullscreen mode Exit fullscreen mode

GET /students/:id 🔒

Includes currently borrowed books with overdue status:

{
  "success": true,
  "data": {
    "_id": "...",
    "name": "Emeka Obi",
    "email": "emeka@school.edu",
    "studentId": "STU001",
    "borrowedBooks": [
      {
        "_id": "...",
        "title": "Things Fall Apart",
        "isbn": "978-0-385-47454-2",
        "returnDate": "2024-06-30T00:00:00.000Z",
        "isOverdue": true
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Attendants

Method Endpoint Auth Description
POST /attendants/login No Login and get token
GET /attendants Yes List all attendants
GET /attendants/:id Yes Get single attendant
POST /attendants Yes Create attendant
PUT /attendants/:id Yes Update attendant
DELETE /attendants/:id Yes Delete attendant

POST /attendants 🔒

{
  "name": "James Godwin",
  "staffId": "STF001",
  "email": "jame@library.edu",
  "password": "secret123"
}
Enter fullscreen mode Exit fullscreen mode

Business Rules

Rule Detail
Borrow Book status must be "IN". Sets status → "OUT", borrowedBy, issuedBy, returnDate.
Return Book status must be "OUT". Clears status → "IN", nullifies borrowedBy, issuedBy, returnDate.
Delete Book Blocked if book is currently "OUT" (borrowed).
Delete Student Blocked if student has active borrowed books.
Duplicate ISBN Returns 409 Conflict if a book with the same ISBN already exists.
Overdue Any book where status = "OUT" and returnDate < now will have isOverdue: true in the response.
Populate on OUT When a book is "OUT", authors, borrowedBy, and issuedBy are always populated in responses.
Auth All write operations and student/attendant reads require a valid JWT (Authorization: Bearer <token>).

Error Reference

Status Meaning
400 Bad request / business rule violated
401 Missing or invalid JWT
404 Resource not found
409 Duplicate key (e.g., ISBN, email)
422 Validation error
500 Internal server error

GitHub Repo: Link

Top comments (0)