Circulation Endpoints
Detailed API reference for circulation operations — issuing books, processing returns, viewing history, and listing active loans.
Circulation Endpoints
Base path: /api/v1/circulation
Authentication: All routes require a valid JWT token.
POST /circulation/issue
Issues a book copy to a user, creating an active loan transaction.
Access Control
- Allowed roles:
ROLE_ADMIN,ROLE_LIBRARIAN
Request
POST /api/v1/circulation/issue
Authorization: Bearer <token>
Content-Type: application/json{
"userId": "64b8f3a2e7d1c...",
"bookCopyId": "64c9a1b2c3d4e..."
}| Field | Type | Required | Description |
|---|---|---|---|
userId | string | ✅ | MongoDB ObjectId of the borrowing user |
bookCopyId | string | ✅ | MongoDB ObjectId of the physical book copy |
What Happens
- Validates the BookCopy exists and has status
AVAILABLE - Updates BookCopy status to
CHECKED_OUT - Creates a Transaction record with:
issuedAt: Current date/timedueDate: 14 days from nowreturnedAt:null(loan is active)
Response — 201 Created
{
"transaction": {
"_id": "64d1f...",
"userId": "64b8f3a2...",
"bookCopyId": "64c9a1b2...",
"issuedAt": "2026-07-03T12:00:00.000Z",
"dueDate": "2026-07-17T12:00:00.000Z",
"returnedAt": null,
"renewalCount": 0
}
}Error Responses
| Status | Body | When |
|---|---|---|
400 | { "error": "Book copy not available" } | Copy doesn't exist or isn't AVAILABLE |
POST /circulation/return
Processes a book return, closing the active transaction.
Access Control
- Allowed roles:
ROLE_ADMIN,ROLE_LIBRARIAN
Request
POST /api/v1/circulation/return
Authorization: Bearer <token>
Content-Type: application/json{
"transactionId": "64d1f..."
}| Field | Type | Required | Description |
|---|---|---|---|
transactionId | string | ✅ | MongoDB ObjectId of the active transaction |
What Happens
- Finds the Transaction by ID
- Validates the transaction exists and hasn't been returned yet
- Sets
returnedAtto current date/time - Updates the associated BookCopy status back to
AVAILABLE
Response — 200 OK
{
"transaction": {
"_id": "64d1f...",
"userId": "64b8f3a2...",
"bookCopyId": "64c9a1b2...",
"issuedAt": "2026-07-03T12:00:00.000Z",
"dueDate": "2026-07-17T12:00:00.000Z",
"returnedAt": "2026-07-10T14:30:00.000Z",
"renewalCount": 0
}
}Error Responses
| Status | Body | When |
|---|---|---|
400 | { "error": "Invalid or already returned transaction" } | Transaction not found or already has returnedAt |
GET /circulation/history
Returns borrowing history, filtered by user role.
Access Control
- Allowed roles: Any authenticated user
- Data filtering: Students see only their own history; Admins/Librarians see all
Request
GET /api/v1/circulation/history
Authorization: Bearer <token>Role-Based Filtering
const where = req.user.role === 'ROLE_STUDENT'
? { userId: req.user.id }
: {};Response — 200 OK
{
"history": [
{
"_id": "64d1f...",
"userId": "64b8f3a2...",
"issuedAt": "2026-07-03T12:00:00.000Z",
"dueDate": "2026-07-17T12:00:00.000Z",
"returnedAt": null,
"bookCopyId": {
"_id": "64c9a...",
"barcode": "B-10045",
"status": "CHECKED_OUT",
"bookId": {
"_id": "64c8e...",
"title": "Clean Code",
"author": "Robert C. Martin"
}
}
}
]
}Population Chain
The response deeply populates bookCopyId → bookId to provide the full book title and metadata without additional API calls.
Sorting
Results are sorted by issuedAt in descending order (newest first).
GET /circulation/active
Returns all currently active (unreturned) loans system-wide.
Access Control
- Allowed roles:
ROLE_ADMIN,ROLE_LIBRARIAN
Request
GET /api/v1/circulation/active
Authorization: Bearer <token>Response — 200 OK
{
"active": [
{
"_id": "64d1f...",
"userId": {
"_id": "64b8f...",
"firstName": "John",
"lastName": "Doe",
"email": "john@university.edu"
},
"bookCopyId": {
"_id": "64c9a...",
"barcode": "B-10045",
"bookId": {
"_id": "64c8e...",
"title": "Clean Code"
}
},
"issuedAt": "2026-07-03T12:00:00.000Z",
"dueDate": "2026-07-17T12:00:00.000Z",
"returnedAt": null
}
]
}Filter
Transaction.find({ returnedAt: null })Only transactions where returnedAt is null (book has not been returned) are included.