A practical Express.js + MongoDB hospital management system with 5 real bugs to find and fix.
This is intentionally built with validation gaps, logic flaws, and data integrity issues for educational purposes.
This is a full Hospital Management System API with interconnected entities:
- Patients - With medical history and appointment tracking
- Doctors - With specialization and hospital assignment
- Appointments - With scheduling and status management
- Hospitals - Managing doctors and departments
Purpose: Workshop participants identify and fix 5 real issues embedded in production-like code.
Stack:
- Express.js 4.18.2
- MongoDB + Mongoose 7.5.0
- Node.js with ES6
# 1. Install dependencies
npm install
# 2. Configure environment
cp .env.example .env
# 3. Start development server
npm run devAPI available at: http://localhost:3000
Database: MongoDB (local or via .env connection string)
GET /api/patients - List all patients (paginated)
GET /api/patients/:id - Get patient details
POST /api/patients - Create new patient
PUT /api/patients/:id - Update patient
DELETE /api/patients/:id - Delete patient
GET /api/doctors - List doctors (filterable by specialization)
GET /api/doctors/:id - Get doctor details
POST /api/doctors - Register doctor
PUT /api/doctors/:id - Update doctor
DELETE /api/doctors/:id - Remove doctor
GET /api/appointments - List all appointments
GET /api/appointments/:id - Get appointment details
POST /api/appointments - Schedule appointment
PUT /api/appointments/:id - Update appointment
DELETE /api/appointments/:id - Cancel appointment
GET /api/hospitals - List all hospitals
GET /api/hospitals/:id - Get hospital details
POST /api/hospitals - Add hospital
PUT /api/hospitals/:id - Update hospital
DELETE /api/hospitals/:id - Remove hospital
All issues are active in the code (no comments marking them). See WORKSHOP_ISSUES.md for detailed explanations.
File: src/models/Doctor.js - phone field
- Phone field accepts any string without format validation
- Impact: Invalid contact info, SMS systems fail
- Difficulty: β Easy
File: src/routes/appointments.js - POST /api/appointments
- No validation that appointment is in future
- Impact: Can schedule fake past appointments
- Difficulty: β Easy
File: src/routes/appointments.js - POST /api/appointments
- No check if doctor already booked at that time
- Impact: Same doctor double-booked multiple times
- Difficulty: ββ Medium
File: src/routes/doctors.js - POST /api/doctors
- Doctor can be assigned to non-existent hospital
- Impact: Orphaned records, broken references
- Difficulty: β Easy
File: src/routes/appointments.js - DELETE /api/appointments/:id
- DELETE endpoint missing response body
- Impact: Client hangs, no confirmation
- Difficulty: β Easy
curl -X POST http://localhost:3000/api/doctors \
-H "Content-Type: application/json" \
-d '{
"firstName": "John",
"lastName": "Smith",
"email": "john@hospital.com",
"phone": "invalid",
"licenseNumber": "LIC123",
"specialization": "Cardiology",
"hospital": "{hospitalId}"
}'
# Should reject but saves successfullycurl -X POST http://localhost:3000/api/appointments \
-H "Content-Type: application/json" \
-d '{
"patient": "{patientId}",
"doctor": "{doctorId}",
"hospital": "{hospitalId}",
"appointmentDate": "2020-01-01T10:00:00Z",
"reason": "Checkup"
}'
# Should reject past date but saves# Schedule first appointment
curl -X POST http://localhost:3000/api/appointments \
-d '{"patient": "{patient1}", "doctor": "{doctorId}", "appointmentDate": "2026-03-15T10:00:00Z"}'
# Schedule same doctor, same time
curl -X POST http://localhost:3000/api/appointments \
-d '{"patient": "{patient2}", "doctor": "{doctorId}", "appointmentDate": "2026-03-15T10:00:00Z"}'
# Both appointments exist - doctor double-bookedcurl -X POST http://localhost:3000/api/doctors \
-H "Content-Type: application/json" \
-d '{
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"phone": "555-1234",
"licenseNumber": "LIC456",
"specialization": "Neurology",
"hospital": "507f1f77bcf86cd799439999"
}'
# Saves despite fake hospital IDcurl -X DELETE http://localhost:3000/api/appointments/{id}
# Request hangs with no responsesrc/
βββ models/ # Mongoose schemas
β βββ Patient.js
β βββ Doctor.js # Issue #1, #4 here
β βββ Appointment.js # Issues #2, #3, #5 here
β βββ Hospital.js
βββ routes/ # API endpoints
β βββ patients.js
β βββ doctors.js # Issue #4 here
β βββ appointments.js # Issues #2, #3, #5 here
β βββ hospitals.js
βββ config/
β βββ database.js # MongoDB connection
βββ server.js # Express app setup
WORKSHOP_ISSUES.md # Detailed bug descriptions
README.md # This file
.env.example # Configuration template
package.json # Dependencies
After fixing all 5 issues, verify:
- Issue #1: Phone validation enforced - add regex pattern to schema
- Issue #2: Appointment date in future enforced - add date comparison check
- Issue #3: Doctor availability checked - verify no conflicting appointments
- Issue #4: Hospital validated - check if hospital exists before saving
- Issue #5: DELETE appointment returns response - add
res.json()statement
// Find this in src/models/Doctor.js
phone: {
type: String,
required: true,
// Missing validation!
}
// Fix: Add phone format validation
phone: {
type: String,
required: true,
match: [/^(\+1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}$/, 'Invalid phone format']
}// Find this in src/routes/appointments.js - POST
const appointment = new Appointment({
appointmentDate, // NO DATE VALIDATION!
// ...
});
// Fix: Check that date is in future
if (new Date(appointmentDate) <= new Date()) {
return res.status(400).json({ message: 'Appointment must be in the future' });
}// Find this in src/routes/appointments.js - POST
const appointment = new Appointment({
doctor,
appointmentDate,
// NO AVAILABILITY CHECK!
});
// Fix: Check if doctor is already booked
const conflict = await Appointment.findOne({
doctor,
appointmentDate,
status: { $ne: 'cancelled' }
});
if (conflict) {
return res.status(400).json({ message: 'Doctor already has appointment at this time' });
}// Find this in src/routes/doctors.js - POST
const doctor = new Doctor({
hospital, // NO CHECK IF EXISTS!
// ...
});
// Fix: Validate hospital exists
if (hospital) {
const hospitalExists = await Hospital.findById(hospital);
if (!hospitalExists) {
return res.status(400).json({ message: 'Hospital not found' });
}
}// Find this in src/routes/appointments.js - DELETE
router.delete('/:id', async (req, res) => {
const appointment = await Appointment.findByIdAndDelete(req.params.id);
if (!appointment) {
return res.status(404).json({ message: 'Not found' });
}
// Missing: res.json() here!
});
// Fix: Add response
res.json({ message: 'Appointment cancelled successfully' });- Phone Validation: Regex for Phone Numbers
- Date Validation: JavaScript Date Comparison
- Query Optimization: MongoDB Queries
- RESTful API Design: Best practices for responses
- Data Integrity: Foreign key and referential integrity
- Read WORKSHOP_ISSUES.md for complete detail on each issue
- Run the API:
npm run dev - Test each issue using curl commands above
- Find the problematic code in
src/ - Fix each issue properly (validation, error handling, etc.)
- Verify your fixes work with test cases
- Commit and document your changes
Important: No comments mark the issue locations - you must find them!
This project demonstrates real-world bugs:
- Data Quality: Missing validation
- API Design: Proper response design
- Logic Bugs: Scheduling conflicts
- Data Integrity: Referential constraints
- Error Handling: Proper client responses
Apply these lessons to prevent bugs in production!
Start here (Easy):
- Issue #1 - Phone validation
- Issue #2 - Time in past check
- Issue #4 - Hospital validation
- Issue #5 - Missing response
Then tackle (Medium):
- Issue #3 - Doctor availability check (requires query logic)
.env file required:
PORT=3000
MONGODB_URI=mongodb://localhost:27017/hospital_appointments
NODE_ENV=developmentCreate from .env.example:
cp .env.example .env- Issues Guide: WORKSHOP_ISSUES.md - Detailed explanations
- Source Code:
src/directory - Find and fix the bugs here - Models:
src/models/- Patient, Doctor, Appointment, Hospital schemas
Educational content for learning purposes.