A full-stack library management system built for Kenyan secondary schools
Digitizing book distribution, borrowing, barcode tracking, and loss reporting β replacing manual record keeping with a fast, role-based system.
- Overview
- System Architecture
- Features
- Tech Stack
- Project Structure
- Getting Started
- API Reference
- Database Schema
- Screenshots
- Deployment
- Roadmap
- Author
The School Library Management System is a multi-platform application designed to replace the manual, paper-based library processes in Kenyan secondary schools. It handles everything from registering books and generating barcodes to tracking distributions, borrowing, and loss reporting.
The system serves three roles:
| Role | Description | Platform |
|---|---|---|
| Librarian | Full system access β manages all books, users, classes, reports | Desktop (React Web App) |
| Teacher | Scans books, manages own stream students, flags losses | Android Mobile App |
| Secretary | Receives printed loss reports from librarian | No system login β paper based |
The school secretary does not have a system login. The librarian generates and downloads loss reports as PDF and forwards them manually.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT LAYER β
β β
β βββββββββββββββββββ ββββββββββββββββββββββββββββ β
β β React Web App β β Android Mobile App β β
β β (Librarian) β β (Teacher) β β
β β localhost:3000 β β Kotlin + ML Kit β β
β ββββββββββ¬βββββββββ βββββββββββββ¬βββββββββββββββ β
β β β β
βββββββββββββΌββββββββββββββββββββββββββββΌβββββββββββββββββββ
β HTTPS / JWT β
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β API LAYER β
β β
β Spring Boot 3.5.x (port 8080) β
β βββββββββββββββββββββββββββββββ β
β β Spring Security + JWT β β
β β 8 REST Controllers β β
β β 40+ Endpoints β β
β β ZXing Barcode Generation β β
β ββββββββββββββββ¬βββββββββββββββ β
β β β
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ
β JDBC / Hibernate
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DATABASE LAYER β
β β
β MySQL 8.0 β
β 9 Tables, Relationships, Indexes β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Register book titles with subject, grade level, and publisher
- Register multiple physical copies at once β up to 500 per batch
- Auto-generate unique Code 128 barcode per physical copy
- View all copies with status β Available, Distributed, Borrowed, Lost
- Select copies and print barcode sheets via browser print dialog
- Filter books by grade level and search by title or subject
- Create grade level classes with academic years
- Add multiple streams per class (7A, 7B, 7C...)
- Assign teachers to streams with visual assignment status
- Expandable class cards showing all streams at a glance
- Librarian creates teacher and librarian accounts
- JWT-based role access control β LIBRARIAN and TEACHER
- Activate and deactivate accounts with confirmation dialogs
- Teacher cards show assigned stream information
- Search and filter users by name, username, or role
- View students by class and stream with cascading selectors
- Stream summary bar showing active count, capacity, and teacher
- Add students with unique admission number enforcement
- Student profile view with basic details
- Active/inactive toggle with full history preservation
- Search by name or admission number
- Assign: Scan barcode β search student β assign in seconds
- Return: Scan barcode β confirm return β one tap
- View Active: All currently distributed books with student info
- End of Term Audit: Find unreturned books β flag as lost from a list
- USB barcode scanner support on desktop β works like a keyboard
- Bluetooth scanner app support for wireless scanning
- Issue: Scan barcode β select student β set due date β issue
- Return: Scan barcode β one-tap confirmation
- Active Borrows: Full list with overdue highlighting
- Overdue Tracking: Automatic detection with days-overdue count
- Flag overdue books as lost β loss report auto-created
- Due date defaults to 2 weeks, fully adjustable
- View pending losses and full report history
- Filter by source β Distribution or Borrowing
- Search any student's loss history by admission number
- Resolve β mark settled with notes (paid, found, etc.)
- Write Off β school absorbs the cost with optional notes
- Download as PDF β formatted report ready to forward to secretary
- Report includes summary table with counts by source and status
- JWT authentication β tokens expire after 24 hours
- Role-based endpoint protection per HTTP method
- Passwords hashed with BCrypt β plain text never stored
- Stateless REST API β no server sessions
- CORS configured for specific origins
| Technology | Version | Purpose |
|---|---|---|
| Java | 25 | Programming language |
| Spring Boot | 3.5.x | REST API framework |
| Spring Security | 6.x | Authentication & authorization |
| JWT (jjwt) | 0.11.5 | Token-based authentication |
| Hibernate / JPA | 6.6.x | ORM β database mapping |
| MySQL | 8.0 | Relational database |
| HikariCP | Built-in | Database connection pooling |
| ZXing | 3.5.2 | Code 128 barcode generation |
| Maven | 3.8+ | Dependency management |
| Technology | Version | Purpose |
|---|---|---|
| React | 18 | UI framework |
| React Router | v6 | Client-side routing |
| Axios | 1.x | HTTP client with interceptors |
| Context API | Built-in | Global authentication state |
| Technology | Purpose |
|---|---|
| Kotlin | Android development language |
| CameraX | Camera preview and image capture |
| ML Kit Barcode Scanning | Real-time Code 128 barcode detection |
| Retrofit2 | HTTP client for API calls |
| Gson | JSON serialization |
| Jetpack Navigation | Fragment navigation |
| ViewModel + LiveData | MVVM architecture |
| SharedPreferences | JWT token storage |
school-library-system/
β
βββ autolibrary/ # Spring Boot Backend
β βββ src/main/java/com/arnold/autolibrary/
β βββ config/
β β βββ SecurityConfig.java # JWT + CORS config
β βββ controller/
β β βββ AuthController.java # Login + registration
β β βββ BookController.java # Books + barcode images
β β βββ BorrowController.java # Library borrows
β β βββ DistributionController.java # Book assignments
β β βββ LossReportController.java # Loss tracking
β β βββ SchoolClassController.java
β β βββ StreamController.java
β β βββ StudentController.java
β β βββ UserDetailsController.java
β βββ model/
β β βββ BookCopy.java
β β βββ BookDetails.java
β β βββ BorrowRecord.java
β β βββ DistributionRecord.java
β β βββ LossReport.java
β β βββ SchoolClass.java
β β βββ Stream.java
β β βββ Student.java
β β βββ UserDetails.java
β β βββ enums/
β β βββ BookStatus.java
β β βββ BorrowStatus.java
β β βββ DistributionStatus.java
β β βββ LossSource.java
β β βββ ResolutionStatus.java
β β βββ Role.java
β βββ repository/ # JPA repositories
β βββ security/
β β βββ CustomUserDetailsService.java
β β βββ JwtAuthFilter.java
β βββ service/ # Business logic
β βββ util/
β βββ BarcodeGenerator.java # Code 128 generation
β βββ JwtUtil.java
β
βββ library-frontend/ # React Frontend (Librarian)
β βββ src/
β βββ context/
β β βββ AuthContext.jsx # Global auth state
β βββ pages/
β β βββ Login.jsx
β β βββ Dashboard.jsx # Stats + quick overview
β β βββ Classes.jsx # Classes + streams
β β βββ Users.jsx # User management
β β βββ Students.jsx # Student management
β β βββ Books.jsx # Books + barcode printing
β β βββ Distributions.jsx # Book assignments
β β βββ Borrows.jsx # Library borrows
β β βββ Losses.jsx # Loss reports + download
β βββ services/
β β βββ api.js # Axios instance + interceptors
β β βββ libraryApi.js # All API service functions
β βββ components/
β β βββ Layout.jsx # Sidebar + navigation
β βββ App.jsx # Routes + protected routes
β
βββ mobileLib/ # Android App (Teacher)
β βββ app/src/main/java/com/arnold/mobileLib/
β βββ data/
β β βββ model/ # Kotlin data classes
β β βββ remote/
β β βββ ApiService.kt # Retrofit interface
β β βββ RetrofitClient.kt # HTTP client setup
β βββ ui/
β β βββ login/ # Login screen
β β βββ home/ # Dashboard fragment
β β βββ students/ # Students list + add
β β βββ scan/ # Camera + barcode scan
β β βββ distributions/ # Active books out
β βββ util/
β β βββ SessionManager.kt # JWT token storage
β β βββ Resource.kt # API state wrapper
β βββ SchoolLibraryApp.kt # Application class
β
βββ README.md
Java 21+ https://adoptium.net/
Maven 3.8+ https://maven.apache.org/
MySQL 8.0+ https://dev.mysql.com/downloads/
Node.js 18+ https://nodejs.org/
Android Studio https://developer.android.com/studio (for mobile)
git clone https://github.com/yourusername/school-library-system.git
cd school-library-system# Create the database
mysql -u root -p -e "CREATE DATABASE school_library;"
# Import the schema
mysql -u root -p school_library < autolibrary/src/main/resources/library_system_v2.sqlOpen autolibrary/src/main/resources/application.properties:
# Database
spring.datasource.url=jdbc:mysql://localhost:3306/school_library?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=your_mysql_username
spring.datasource.password=your_mysql_password
# JWT β change this secret in production
jwt.secret=schoollibrarySystemSecretKeyForJWTTokenGenerationMustBeLong
jwt.expiration=86400000
# Server port
server.port=8080cd autolibrary
mvn spring-boot:runConfirm startup is successful β the console should show:
SECURITY CONFIG LOADED
Started AutolibraryApplication in X seconds
β οΈ The lineUsing generated security passwordshould not appear. If it does, verifyCustomUserDetailsService.javaexists in thesecuritypackage.
POST http://localhost:8080/api/auth/register-librarian
Content-Type: application/json
{
"fullName": "Head Librarian",
"userName": "librarian",
"password": "yourpassword"
}This endpoint only works once. A second call returns an error if a librarian exists.
cd library-frontend
npm install
npm startOpen http://localhost:3000 and log in with your librarian credentials.
Open mobileLib/ in Android Studio.
Update the base URL in RetrofitClient.kt:
// For Android emulator
private const val BASE_URL = "http://10.0.2.2:8080/api/"
// For physical device on same WiFi β use your PC's local IP
private const val BASE_URL = "http://192.168.1.x:8080/api/"Connect your Android device with USB Debugging enabled and click Run.
| Method | Endpoint | Access | Description |
|---|---|---|---|
POST |
/api/auth/login |
Public | Login β returns JWT token |
POST |
/api/auth/register-librarian |
Public | Create first librarian (once only) |
All other endpoints require:
Authorization: Bearer <your_jwt_token>| Method | Endpoint | Access |
|---|---|---|
GET |
/api/classes |
Both |
POST |
/api/classes |
Librarian |
GET |
/api/streams/class/{classId} |
Both |
POST |
/api/streams?classId={id} |
Librarian |
PUT |
/api/streams/{id}/teacher?userId={id} |
Librarian |
| Method | Endpoint | Access |
|---|---|---|
GET |
/api/users |
Librarian |
POST |
/api/users |
Librarian |
GET |
/api/users/role/{role} |
Librarian |
PUT |
/api/users/{id}/deactivate |
Librarian |
PUT |
/api/users/{id}/activate |
Librarian |
PUT |
/api/users/{id}/stream?streamId={id} |
Librarian |
| Method | Endpoint | Access |
|---|---|---|
GET |
/api/students/stream/{streamId} |
Both |
POST |
/api/students?streamId={id} |
Both |
GET |
/api/students/{id} |
Both |
GET |
/api/students/admission/{admNo} |
Both |
PUT |
/api/students/{id}/transfer?newStreamId={id} |
Both |
PUT |
/api/students/{id}/deactivate |
Both |
| Method | Endpoint | Access |
|---|---|---|
GET |
/api/books |
Both |
POST |
/api/books |
Librarian |
POST |
/api/books/{id}/copies?quantity={n}&dateAcquired={date} |
Librarian |
GET |
/api/books/copies/{bookId} |
Both |
GET |
/api/books/scan/{qrCode} |
Both |
GET |
/api/books/copies/{copyId}/qr-image |
Both |
| Method | Endpoint | Access |
|---|---|---|
POST |
/api/distributions |
Both |
PUT |
/api/distributions/return/{qrCode} |
Both |
GET |
/api/distributions/year/{year} |
Both |
GET |
/api/distributions/stream/{streamId}/year/{year} |
Both |
POST |
/api/distributions/loss |
Both |
| Method | Endpoint | Access |
|---|---|---|
POST |
/api/borrows |
Both |
PUT |
/api/borrows/return/{qrCode} |
Both |
GET |
/api/borrows/active |
Both |
GET |
/api/borrows/overdue |
Both |
POST |
/api/borrows/loss |
Both |
| Method | Endpoint | Access |
|---|---|---|
GET |
/api/losses |
Both |
GET |
/api/losses/pending |
Both |
GET |
/api/losses/student/{studentId} |
Both |
GET |
/api/losses/source/{source} |
Both |
PUT |
/api/losses/{id}/resolve |
Librarian |
PUT |
/api/losses/{id}/writeoff |
Librarian |
school_class
classId, className, gradeLevel, academicYear
stream
streamId, streamName, classId (FK), teacherId (FK), capacity, isActive
user_details
userId, fullName, userName, passwordHash, role (LIBRARIAN|TEACHER),
streamId (FK), isActive, createdAt
student
studentId, admissionNumber (UNIQUE), fullName, streamId (FK),
yearEnrolled, isActive, createdAt
book_details
detailsId, titleName, subject, gradeLevel, publisher, copies, createdAt
book_copy
bookId, detailsId (FK), qrCode (UNIQUE), status (AVAILABLE|BORROWED|
DISTRIBUTED|LOST), isActive, dateAcquired
distribution_record
distributionId, bookId (FK), studentId (FK), dateDistributed,
dateReturned, academicYear, distributedBy (FK), status
borrow_record
borrowId, bookId (FK), studentId (FK), dateBorrowed, dateDue,
dateReturned, issuedBy (FK), status (ACTIVE|RETURNED|OVERDUE|LOST)
loss_report
reportId, copyId (FK), studentId (FK), dateFlagged,
source (DISTRIBUTION|BORROWING), reason,
resolutionStatus (PENDING|RESOLVED|WRITTEN_OFF),
dateResolved, notes
The system uses Code 128 barcodes β the library industry standard.
Each book copy gets a unique ID:
Format: BOOK-{titleId}-{random8chars}
Example: BOOK-3-A1B2C3D4
A USB barcode scanner connects as a keyboard device:
1. Librarian clicks the scan input field
2. Points scanner at book barcode
3. Scanner reads Code 128 β types value β presses Enter
4. System instantly looks up the book
No driver or special software needed β works out of the box.
The Android app uses Google ML Kit with the device camera:
1. Teacher opens Scan tab
2. Camera preview opens automatically
3. Points phone camera at barcode
4. ML Kit detects Code 128 in real time
5. Book info appears on screen
| Option | How it works | Cost |
|---|---|---|
| Bluetooth Scanner App | Android app pairs as wireless keyboard scanner | Free app |
| USB OTG | Phone connects as USB camera | USB OTG cable |
| IP Webcam | Phone camera streamed over WiFi | Free app |
| Feature | Librarian | Teacher |
|---|---|---|
| Create/manage users | β | β |
| Manage classes & streams | β | β |
| Register books & generate barcodes | β | β |
| Print barcode sheets | β | β |
| Add students | β | β (own stream only) |
| Scan to assign books | β | β |
| Scan to return books | β | β |
| Issue library borrows | β | β |
| Flag books as lost | β | β |
| Resolve loss reports | β | β |
| Download PDF reports | β | β |
| View all streams | β | β (own stream only) |
Run everything on one school computer. Teachers connect via school WiFi.
Server: Any Windows/Linux/Mac computer
Cost: KES 0/month
Access: School WiFi only
Deploy on a VPS with Nginx as reverse proxy.
Providers: DigitalOcean, Hetzner, Railway
Cost: KES 600β800/month
Access: Anywhere with internet
Internet β Nginx β React (port 80/443)
β Spring Boot (port 8080)
β MySQL (port 3306, internal only)
Frontend β Vercel (free)
Backend β Railway (free tier)
Database β Railway MySQL (free tier β 1GB)
# Free SSL with Let's Encrypt (production only)
sudo certbot --nginx -d yourdomain.co.ke- Full REST API with JWT security
- React desktop frontend β all 8 pages
- Code 128 barcode generation and printing
- Book distribution with scan support
- Library borrowing with overdue tracking
- Loss reports with PDF download
- Android mobile app β login, students, scan, distributions
- Mobile app polish β logout enforcement, deactivation blocking
- Barcode scanning reliability improvements
- Bulk student import from CSV
- Student profile page with full history
- Academic year management settings
- Change password feature
- Push notifications for overdue borrows
- Multi-school SaaS support
- Student transfer history logging
- Export class lists to Excel/PDF
- Stream capacity count does not update dynamically when students are added β workaround: reload the page
- Mobile app stream assignment requires fresh login after assigning teacher to stream in the web app
- Loss reports page pending count in dashboard requires a page refresh after resolving
- Fork the repository
- Create a feature branch
git checkout -b feature/your-feature-name- Commit your changes
git commit -m "Add: description of change"- Push and open a Pull Request
git push origin feature/your-feature-nameThis project is proprietary software. All rights reserved Β© 2025 Arnold.
Arnold
Built from scratch over several weeks β backend, frontend, and Android mobile app. Designed specifically for the operational realities of Kenyan secondary school libraries.
- Backend: Java 25 + Spring Boot 3.5.x
- Frontend: React 18
- Mobile: Kotlin + Android
- Database: MySQL 8.0
- Spring Boot β backend framework
- ZXing β barcode generation and scanning
- React β frontend framework
- ML Kit β mobile barcode scanning
- jjwt β JWT library
- Retrofit β Android HTTP client
- HikariCP β database connection pooling