This is a robust RESTful API for a Library Management System, built with Node.js, Express, and PostgreSQL. It features a clean, separated architecture (MVC + Services), transactional integrity for borrowing, and a CQRS (Command Query Responsibility Segregation) pattern for efficient book management.
- User Management: Full CRUD (Create, Read, Update, Delete) operations for library users.
- Book Management: Full CRUD operations for books. Supports adding single books or batches of books in one request.
- Borrowing & Returns: Transaction-safe system for users to borrow and return books.
- Advanced Book Search: A dedicated read model allows for complex queries, including:
- Dynamic filtering by title, author, genre, publisher, UUID, or ISBN.
- Filtering by status (
availableorborrowed). - Aggregation of all active loan details (borrower, due date) nested within each book object.
- Business Rule Enforcement:
- Overdue Prevention: Users with overdue books are blocked from borrowing new items.
- Borrowing Limit: Users are limited to a maximum of 3 concurrent borrows.
- Due Dates: Books are automatically assigned a 14-day due date upon borrowing.
- Availability Check: Real-time checking of book quantity vs. borrowed count prevents over-borrowing. This check is also performed inside the database transaction to prevent race conditions.
- Secure & Unique IDs:
- Users: Uses a custom, human-readable, and time-based public ID (e.g.,
25301-A4FGHJ). - Books & Transactions: Uses standard, cryptographically secure UUIDs (v4).
- Users: Uses a custom, human-readable, and time-based public ID (e.g.,
This project follows a modern Node.js application structure, separating concerns for maintainability and scalability.
-
MVC (Model-View-Controller) Pattern:
- Routes (
.routes.mjs): Define the API endpoints and map them to the appropriate controller functions. - Controllers (
.ctrl.mjs): Handle incoming HTTP requests, validate input, and orchestrate the business logic by calling services and models. They are responsible for sending the final HTTP response. - Models (
.model.mjs): Represent the data layer. All database interaction (SQL queries) is encapsulated within the model files, usingpg-promiseto communicate with the PostgreSQL database.
- Routes (
-
Service Layer (
.service.mjs):- Used to abstract complex business logic and helper functions away from the controllers.
- Book Service: Enforces business rules, such as preventing updates to protected columns (e.g.,
created_at) and validating thatuuidandisbnmatch the same book before an update or delete operation. - ID Service: A dedicated service for generating custom user IDs and standard UUIDs.
-
CQRS (Command Query Responsibility Segregation):
- This pattern is explicitly used for the
booksresource. - Commands (Write): Create, Update, and Delete operations are handled by
cud_books.model.mjs. These are simple, direct operations. - Queries (Read): All read operations are handled by
read_books.model.mjs. This file contains a more complex, optimized SQL query designed to join, aggregate, and calculate data (likeavailable_quantityand nestedloans) efficiently for display.
- This pattern is explicitly used for the
-
Database (
db.mjs):- Uses
pg-promiseto manage the PostgreSQL database connection. - Employs database transactions (
db.tx) in critical operations like borrowing books to ensure data integrity. If any part of the borrow process fails, the entire transaction is rolled back.
- Uses
Route prefix: /api/users
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Retrieves a list of all users. Can also be used to find a single user by query parameters (?public_id= or ?email=). |
POST |
/ |
Registers a new user. Requires name and email in the body. |
PATCH |
/:public_id |
Updates an existing user's details. Can provide newName and/or newEmail in the body. |
DELETE |
/:public_id |
Deletes a user by their public_id. |
Route prefix: /api/books
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Retrieves a list of all books. Supports extensive query filters: ?title=, ?author=, ?genre=, ?publisher=, ?uuid=, ?isbn=, and ?status=available or ?status=borrowed. |
POST |
/ |
Adds a new book or a batch of books. The body can be a single book object or an array of book objects. |
PATCH |
/ |
Updates a book. Requires either uuid or isbn in the body, along with an edit_book_stack array of changes. |
DELETE |
/ |
Deletes a book. Requires either uuid or isbn in the request. |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/borrow |
Borrows one or more books. The body must contain the user's public_id and an array of books (each with a uuid) to be borrowed. |
PATCH |
/api/return |
Returns one or more books. The body must be an array of transaction_id objects to be returned. |
Based on the models, the following database tables are expected:
| Column | Type | Description |
|---|---|---|
public_id |
VARCHAR |
Primary Key. The custom-generated ID (e.g., 25301-A4FGHJ). |
name |
VARCHAR |
User's full name. |
email |
VARCHAR |
User's email (unique). |
created_at |
TIMESTAMP |
Timestamp of user creation. |
last_modified |
TIMESTAMP |
Timestamp of last user update. |
| Column | Type | Description |
|---|---|---|
uuid |
UUID |
Primary Key. The unique identifier for the book. |
title |
VARCHAR |
The title of the book. |
author |
VARCHAR |
The author of the book. |
publisher |
VARCHAR |
The book's publisher. |
genre |
VARCHAR |
The genre of the book. |
isbn |
VARCHAR |
The 13-digit ISBN (unique). |
quantity |
INTEGER |
The total number of copies of this book in the library. |
created_at |
TIMESTAMP |
Timestamp of book creation. |
last_modified |
TIMESTAMP |
Timestamp of last book update. |
| Column | Type | Description |
|---|---|---|
transaction_id |
UUID |
Primary Key. The unique ID for this borrow event. |
user_id |
VARCHAR |
Foreign Key. References users.public_id. |
book_id |
UUID |
Foreign Key. References books.uuid. |
borrow_date |
TIMESTAMP |
The exact date and time the book was borrowed. |
due_date |
TIMESTAMP |
The date the book is due (borrow_date + 14 days). |
return_date |
TIMESTAMP |
NULL if the book is still borrowed. Set to NOW() when returned. |
-
Clone the repository:
git clone <repository-url> cd <repository-name>
-
Install dependencies:
npm install
-
Set up environment variables: Create a
.envfile in the root of the project and add your PostgreSQL connection details:PSQL_USER=your_postgres_user PSQL_PASS=your_postgres_password PSQL_DB=your_library_database_name
(Note: The code also references
localhost:5600as the database host/port.) -
Run the application:
npm start