Experience the live application: LibraFlow
Frontend Repository: LibManagement-FE
This application is professionally deployed using modern cloud infrastructure:
- Backend API: Containerized with Docker and deployed on Render
- Database: Production-grade PostgreSQL hosted on Neon serverless platform
- Frontend: Static deployment on Netlify with CDN
💡 Full-Stack Deployment: Demonstrates expertise in containerization (Docker), cloud deployment (Render, Neon), CI/CD pipelines, and modern DevOps practices.
A RESTful API-based Library Management System built with Spring Boot for managing books, members, staff, and lending operations.
- Overview
- Features
- Technologies Used
- Prerequisites
- Installation & Setup
- API Endpoints
- Configuration
- Project Structure
- Database Schema
- Usage Examples
- Troubleshooting
- Contributing
The Library Management System is a comprehensive backend application that provides REST APIs to manage library operations including:
- Book inventory management
- Member registration and management
- Staff management
- Book lending and return operations
- Fine calculation for overdue books
Version: 1.0.0
Application Context Path: /booklib
Default Port: 8081 (dev profile)
-
Authentication & Security
- JWT-based authentication
- BCrypt password encryption
- Role-based access control (ADMIN, LIBRARIAN, OFFICER)
- Automatic default admin user creation on startup
- Secure API endpoints with token validation
-
Book Management
- Add, update, delete, and search books
- Track total and available quantities
- Automatic inventory updates on lending/return
-
Member Management
- Register new members
- Update member information
- Track membership dates
-
Staff Management
- Add and manage library staff
- Role-based access (via Role enum)
- Secure password management with BCrypt
-
Lending Operations
- Issue books to members
- Track lending dates and return dates
- Automatic book quantity deduction
- Fine calculation for overdue returns
-
Additional Features
- Health check endpoint
- Custom logging with Logback
- Profile-based configuration (dev, prod)
- Custom application banner
- CORS configuration for frontend integration
- Java 21
- Spring Boot 3.5.6
- Spring Web (REST API)
- Spring Data JPA (Data Access)
- Spring Security (Authentication & Authorization)
- Spring MVC
- JWT (JSON Web Tokens) - JJWT 0.12.3
- Hibernate (ORM)
- Lombok (Boilerplate reduction)
- ModelMapper (DTO-Entity mapping)
- Log4j2 (Logging)
- Maven (Build tool)
- Development: MySQL 8.0
- Production: PostgreSQL 16 (Neon Serverless)
- Containerization: Docker
- Backend Hosting: Render (Container deployment)
- Database Hosting: Neon (Serverless PostgreSQL)
- Frontend Hosting: Netlify (CDN-powered)
- Version Control: Git & GitHub
- Java Development Kit (JDK) 21 or higher
- Maven 3.6+
- MySQL 8.0+ or PostgreSQL 16+ installed and running
- Git (for cloning)
- Docker & Docker Compose (for containerization)
- Render Account (for backend hosting)
- Neon Account (for PostgreSQL database)
- Netlify Account (for frontend hosting)
The application is deployed using a modern, scalable cloud architecture:
- Platform: Render Web Service
- Deployment Method: Docker Container
- Auto-Deploy: GitHub integration with automatic deployments on push
- Health Checks: Configured health endpoints for zero-downtime deployments
- Environment: Production-ready with environment variable management
- Platform: Neon Serverless PostgreSQL
- Features:
- Auto-scaling based on demand
- Built-in connection pooling
- Instant provisioning
- Branching for development environments
- Benefits: Cost-effective, scales to zero when idle, instant cold starts
- Platform: Netlify CDN
- Deployment: Continuous deployment from GitHub
- Features: Global CDN, instant cache invalidation, custom domain support
GitHub Push → Docker Build → Render Deploy → Neon PostgreSQL
↓
Netlify Frontend
git clone <repository-url>
cd LibManagementCreate a MySQL database or let the application auto-create it:
CREATE DATABASE booklib2025;Edit src/main/resources/application-dev.properties:
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.url=jdbc:mysql://localhost:3306/booklib2025?createDatabaseIfNotExist=trueEdit src/main/resources/application-prod.properties:
spring.datasource.url=jdbc:postgresql://<your-neon-host>.neon.tech:5432/booklib_prod?sslmode=require
spring.datasource.username=<neon_username>
spring.datasource.password=<neon_password>
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialectmvnw clean installmvnw spring-boot:runThe application will start at: http://localhost:8081/booklib
Build Docker Image:
docker build -t libraflow-backend:latest .Run with Docker:
docker run -p 8081:8081 \
-e SPRING_PROFILES_ACTIVE=prod \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://your-neon-host:5432/booklib \
-e SPRING_DATASOURCE_USERNAME=your_username \
-e SPRING_DATASOURCE_PASSWORD=your_password \
-e JWT_SECRET=your_jwt_secret \
libraflow-backend:latestDocker Compose (Full Stack):
docker-compose up -d🐳 Production Note: The Render deployment uses the Dockerfile for automated container builds and deployments.
- Production API:
https://your-app.onrender.com/booklib(Deployed on Render) - Local Development:
http://localhost:8081/booklib - Frontend:
https://libraflowmgmt.netlify.app(Deployed on Netlify)
| Method | Endpoint | Description | Public |
|---|---|---|---|
| POST | /api/auth/login |
User login - returns JWT token | ✅ |
| POST | /api/auth/validate |
Validate JWT token | ✅ |
Default Admin Credentials:
- Username:
admin - Password:
admin123 - Role:
ADMIN
GET /api/v1/health
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/books |
Add a new book |
| GET | /api/v1/books |
Get all books |
| GET | /api/v1/books?bookId={id} |
Get book by ID |
| PATCH | /api/v1/books?bookId={id} |
Update a book |
| DELETE | /api/v1/books?bookId={id} |
Delete a book |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/members |
Add a new member |
| GET | /api/v1/members |
Get all members |
| GET | /api/v1/members?memberId={id} |
Get member by ID |
| PATCH | /api/v1/members?memberId={id} |
Update a member |
| DELETE | /api/v1/members?memberId={id} |
Delete a member |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/staff |
Add a new staff member |
| GET | /api/v1/staff |
Get all staff |
| GET | /api/v1/staff?staffId={id} |
Get staff by ID |
| PATCH | /api/v1/staff?staffId={id} |
Update staff |
| DELETE | /api/v1/staff?staffId={id} |
Delete staff |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/lendings |
Issue a book |
| GET | /api/v1/lendings |
Get all lending records |
| GET | /api/v1/lendings?lendingId={id} |
Get lending by ID |
| PATCH | /api/v1/lendings?lendingId={id} |
Update lending (return) |
| DELETE | /api/v1/lendings?lendingId={id} |
Delete lending record |
POST http://localhost:8081/booklib/api/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "admin123"
}Response:
{
"jwt": "eyJhbGciOiJIUzI1NiJ9...",
"username": "admin"
}POST http://localhost:8081/booklib/api/v1/books
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
{
"bookName": "Java Programming",
"author": "John Doe",
"edition": "3rd",
"publisher": "Tech Publications",
"isbn": "978-1234567890",
"price": 1500.00,
"totalQty": 10,
"availableQty": 10
}📝 Note: All endpoints except
/api/auth/**require a valid JWT token in the Authorization header.
POST http://localhost:8081/booklib/api/v1/staff
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "0771234567",
"role": "LIBRARIAN",
"username": "johndoe",
"password": "securePassword123"
}🔒 Passwords are automatically encrypted using BCrypt before storage.
POST http://localhost:8081/booklib/api/v1/members
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
{
"name": "John Doe",
"email": "johndoe@gmail.com"
}POST http://localhost:8081/booklib/api/v1/lendings
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
{
"bookId": "B001",
"memberId": "M001",
"lendingDate": "2025-10-20"
}The application supports multiple profiles:
- dev (Development) - Default active profile
- prod (Production)
application.properties:
spring.application.name=LibManagement
server.servlet.context-path=/booklib
spring.profiles.active=devapplication-dev.properties:
server.port=8081
perDayFine=5.0
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# JWT Configuration
jwt.secret=G8rx3T9d3B5zpyzsyufiI6Kw/0dxT9cWZS6hE2GoqJU=
jwt.expiration=86400000 # 24 hours in milliseconds- JWT Token Expiration: 24 hours (configurable)
- Password Encryption: BCrypt with strength 10
- CORS: Enabled for
http://localhost:3000andhttp://localhost:5173 - Session Management: Stateless (JWT-based)
Default fine per day for overdue books: Rs. 5.00 (configurable in application-dev.properties)
LibManagement/
├── src/
│ ├── main/
│ │ ├── java/lk/puLeeNa/LibManagement/
│ │ │ ├── config/ # Configuration Classes
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ ├── CORSConfig.java
│ │ │ │ └── DataInitializer.java
│ │ │ ├── controller/ # REST Controllers
│ │ │ │ ├── AuthController.java
│ │ │ │ ├── BookControler.java
│ │ │ │ ├── MemberController.java
│ │ │ │ ├── StaffController.java
│ │ │ │ ├── LengdingController.java
│ │ │ │ └── HealthTest.java
│ │ │ ├── dao/ # Data Access Objects (Repositories)
│ │ │ │ ├── BookDao.java
│ │ │ │ ├── MemberDao.java
│ │ │ │ ├── StaffDao.java
│ │ │ │ └── LendingDao.java
│ │ │ ├── dto/ # Data Transfer Objects
│ │ │ │ ├── BookDTO.java
│ │ │ │ ├── MemberDTO.java
│ │ │ │ ├── StaffDTO.java
│ │ │ │ ├── LendingDTO.java
│ │ │ │ └── Role.java
│ │ │ ├── entities/ # JPA Entities
│ │ │ │ ├── BookEntity.java
│ │ │ │ ├── MemberEntity.java
│ │ │ │ ├── StaffEntity.java
│ │ │ │ └── LendingEntity.java
│ │ │ ├── exception/ # Custom Exceptions
│ │ │ │ ├── BookNotFoundException.java
│ │ │ │ ├── MemberNotFoundException.java
│ │ │ │ ├── StaffNotFoundException.java
│ │ │ │ ├── LendingDataNotFoundException.java
│ │ │ │ ├── DataPersistException.java
│ │ │ │ └── EnoughBooksNotFoundException.java
│ │ │ ├── security/ # Security Components
│ │ │ │ └── JwtRequestFilter.java
│ │ │ ├── service/ # Business Logic
│ │ │ │ ├── impl/
│ │ │ │ ├── BookService.java
│ │ │ │ ├── MemberService.java
│ │ │ │ ├── StaffService.java
│ │ │ │ ├── LendingService.java
│ │ │ │ └── CustomUserDetailsService.java
│ │ │ ├── util/ # Utility Classes
│ │ │ │ ├── EntityDTOConvert.java
│ │ │ │ ├── JwtUtil.java
│ │ │ │ ├── LendingMapping.java
│ │ │ │ └── UtilData.java
│ │ │ └── LibManagementApplication.java
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── application-dev.properties
│ │ ├── application-prod.properties
│ │ ├── banner.txt
│ │ └── logback-spring.xml
│ └── test/
├── Logs/ # Application logs
├── pom.xml
└── README.md
The application uses the following main entities:
- bookId (PK)
- bookName
- author
- edition
- publisher
- isbn
- price
- totalQty
- availableQty
- lastUpdateDate
- lastUpdateTime
- memberId (PK)
- name
- membershipDate
- staffId (PK)
- firstName
- lastName
- username (unique)
- password (BCrypt encrypted)
- role (ADMIN, LIBRARIAN, OFFICER)
- phone
- joinDate
- lastUpdateDate
- lastUpdateTime
- lendingId (PK)
- bookId (FK)
- memberId (FK)
- lendingDate
- returnDate
- fine
- Prepare Dockerfile (already included in project)
- Push to GitHub
- Create Render Web Service:
- Connect GitHub repository
- Select Docker runtime
- Configure environment variables
- Set health check path:
/booklib/api/v1/health
- Auto-Deploy: Render automatically builds and deploys on git push
- Create Neon Project:
- Sign up at neon.tech
- Create new PostgreSQL database
- Copy connection string
- Configure in Render:
- Add
SPRING_DATASOURCE_URLenvironment variable - Enable connection pooling for optimal performance
- Add
- Run Migrations: Tables auto-create on first deploy (ddl-auto=update)
- Build React App:
npm run build - Connect to Netlify:
- Link GitHub repository
- Set build command and output directory
- Configure API proxy/CORS settings
- Environment Variables: Set backend API URL
- Deploy: Automatic deployment on git push
graph LR
A[Git Push] --> B[GitHub]
B --> C[Render Build]
C --> D[Docker Image]
D --> E[Deploy Container]
E --> F[Neon PostgreSQL]
B --> G[Netlify Build]
G --> H[Deploy Frontend]
H --> I[CDN Distribution]
- Login: Send credentials to
/api/auth/login - Receive Token: Get JWT token in response
- Use Token: Include token in
Authorization: Bearer <token>header for all subsequent requests - Token Expiration: Token expires after 24 hours (configurable)
All endpoints except /api/auth/** require authentication. Add the JWT token to the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTYzMjQ...
- ADMIN: Full access to all operations
On the first application startup:
- Default admin user is created automatically
- Credentials are logged in the console
- Login with default credentials
- Important: Change the default password immediately after first login
1. Authentication Failed Error
401 Unauthorized - Invalid username or password
Solution: Verify credentials. Use default admin credentials on first login: username=admin, password=admin123
2. JWT Token Expired
401 Unauthorized - Token expired
Solution: Login again to get a new token. Tokens expire after 24 hours.
3. Missing Authorization Header
403 Forbidden - Access Denied
Solution: Include the JWT token in the Authorization header: Bearer <your-token>
4. Missing Request Parameter Error
Required request parameter 'bookId' for method parameter type String is not present
Solution: Ensure you're passing the required query parameters in your requests.
2. Update/Delete Query Error
Query executed via 'getResultList()' must be a 'select' query
Solution: Add @Modifying and @Transactional annotations to custom query methods in DAO that perform UPDATE or DELETE operations.
3. ModelMapper Configuration Error
The destination property matches multiple source property hierarchies
Solution: Configure ModelMapper with custom type maps or use manual mapping for complex relationships.
4. Transaction Required Error
Executing an update/delete query requires a transaction
Solution: Add @Transactional annotation to service methods that modify data.
- Verify MySQL is running
- Check database credentials in
application-dev.properties - Ensure the database exists or
createDatabaseIfNotExist=trueis set
If port 8081 is already in use, change it in application-dev.properties:
server.port=8082Application logs are stored in the Logs/ directory. Configure logging in logback-spring.xml.
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
puLeeNa
Library Management System - 2025
For issues and questions, please create an issue in the repository.
-
🐳 Docker Containerization:
- Ensures consistency across environments
- Easy scaling and orchestration
- Platform-independent deployment
-
☁️ Render for Backend:
- Zero-downtime deployments
- Automatic SSL/TLS certificates
- Built-in DDoS protection
- Docker-native platform
-
⚡ Neon Serverless PostgreSQL:
- Auto-scaling database
- No idle costs (scales to zero)
- Instant database branching for testing
- Built-in connection pooling
-
🌐 Netlify for Frontend:
- Global CDN distribution
- Instant cache invalidation
- Atomic deployments
- Preview deployments for PRs
- API Response Time: < 200ms average
- Global CDN Coverage: 100+ edge locations
- Uptime: 99.9% SLA
- Cold Start: < 2s (Neon database)
Built with ❤️ using Spring Boot | Deployed with 🚀 Docker, Render, Neon & Netlify