A full-stack web application that allows university students to browse campus services, book appointments, and manage their schedules. Staff providers manage their availability and approve/reject requests; admins oversee users, services, and reporting.
Built for CPTS 489 – Web Application Development, Washington State University.
- Tech Stack
- Project Structure
- Prerequisites
- Getting Started
- Environment Variables
- Database Commands
- Running Tests
- API Overview
- System Architecture
- User Roles
- Demo Accounts
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, Vite, React Router v6, TanStack Query, React Hook Form, Zod, Bootstrap 5, Axios |
| Backend | Node.js, Express 4, TypeScript, Sequelize 6 ORM |
| Database | MySQL 8 |
| Auth | JWT (Bearer token) + bcryptjs |
| Shared | Zod schemas in an npm workspace consumed by both client and server |
| Testing | Jest + Supertest (server), Vitest + Testing Library + MSW (client) |
| Build | npm workspaces monorepo |
StudentBookingServices/
├── client/ # React SPA (Vite)
├── server/ # Express REST API
│ ├── src/
│ │ ├── controllers/ # Thin request handlers
│ │ ├── services/ # Business logic (auth, booking conflict detection)
│ │ ├── models/ # Sequelize models
│ │ ├── routes/ # Express routers
│ │ ├── middleware/ # Auth, role checks, validation, error handling
│ │ ├── dto/ # Response serialization
│ │ └── utils/ # JWT, password, date helpers
│ └── db/
│ ├── migrations/ # Sequelize CLI migrations
│ └── seeders/ # Demo data seeder
└── shared/ # Zod schemas and TypeScript types (used by client + server)
- Node.js 20 or higher
- npm 10 or higher (comes with Node 20)
- MySQL 8 running locally (or via Docker)
From the project root, install all workspace packages at once:
npm installcp server/.env.example server/.env
cp client/.env.example client/.envEdit server/.env with your MySQL credentials (see Environment Variables below).
cd server
npm run db:reset # Creates tables and loads demo dataOpen two terminals:
# Terminal 1 – API server (http://localhost:4000)
cd server
npm run dev
# Terminal 2 – Frontend (http://localhost:5173)
cd client
npm run devThe Vite dev server proxies all /api/* requests to localhost:4000, so no CORS configuration is needed during development.
# From the project root
npm run build
# Start the compiled server
node server/dist/index.jsServe the client/dist/ directory from a static file host or configure Express to serve it.
| Variable | Default | Description |
|---|---|---|
PORT |
4000 |
Express server port |
DB_HOST |
localhost |
MySQL host |
DB_PORT |
3306 |
MySQL port |
DB_NAME |
student_booking |
Database name |
DB_USER |
root |
MySQL user |
DB_PASS |
(empty) | MySQL password |
JWT_SECRET |
(required) | Secret used to sign JWTs |
JWT_EXPIRES_IN |
7d |
Token lifespan |
NODE_ENV |
development |
development / production / test |
| Variable | Default | Description |
|---|---|---|
VITE_API_BASE_URL |
/api |
API base path (proxied in dev) |
All commands are run from the server/ directory.
| Command | Description |
|---|---|
npm run db:migrate |
Apply pending migrations |
npm run db:seed |
Insert demo data |
npm run db:reset |
Drop all tables, re-migrate, re-seed |
# Run all tests (server + client)
npm run test
# Server tests only (Jest + Supertest)
npm run test:server
# Client tests only (Vitest)
npm run test:clientServer tests require a running MySQL instance. The test suite uses a separate test database configured via DB_NAME_TEST in server/.env.
The API is versioned under /api and returns JSON. All protected routes require an Authorization: Bearer <token> header.
Error responses follow a consistent shape:
{
"code": "VALIDATION_ERROR",
"message": "Human-readable description",
"fieldErrors": { "email": "Invalid email address" }
}| Group | Prefix | Description |
|---|---|---|
| Auth | /api/auth |
Register, login, password reset |
| Users | /api/users |
Profile read/update |
| Services | /api/services |
Browse services, check availability |
| Bookings | /api/bookings |
Create, view, reschedule, cancel, approve/reject |
| Provider | /api/provider |
Manage availability slots and incoming requests |
| Notifications | /api/notifications |
In-app alert feed |
| Admin | /api/admin |
User management, service CRUD, KPI reports |
See IMPLEMENTATION_PLAN.md for the full table of all 32 endpoints.
The application follows a three-tier architecture within an npm workspaces monorepo.
Requests flow through a layered pipeline:
Request → Middleware (auth, role, validation) → Controller → Service → Model → Database
- Controllers are thin; they extract request data and delegate to services.
- Services hold all business logic (e.g.
BookingServicedetects scheduling conflicts using aSERIALIZABLEdatabase transaction). - Models are plain Sequelize definitions with no business logic.
- Middleware handles cross-cutting concerns: JWT verification (
requireAuth), role gating (requireRole), Zod validation (validate), and centralised error formatting (errorHandler).
- React Router v6 handles client-side navigation with a
ProtectedRoutewrapper that redirects unauthenticated or unauthorised users. - TanStack Query manages all server state (fetching, caching, background refetching, and cache invalidation after mutations).
- React Hook Form + Zod handles form state and validation, sharing the same schemas as the backend via the
sharedpackage. - AuthContext stores the current user, JWT token, and role in React context; the Axios interceptor attaches the token to every request automatically.
The shared/ workspace publishes Zod schemas and their inferred TypeScript types. Both the server validation middleware and client form validation import from this package, ensuring the validation rules are never duplicated or out of sync.
| Table | Purpose |
|---|---|
users |
Accounts with role (student, staff, admin), is_banned soft-delete flag |
service_categories |
Lookup table (Advising, Library, Counselling, etc.) |
services |
Provider-owned bookable services with duration and location |
availability_blocks |
Provider time slots linked to a service |
bookings |
Appointments linking a student to a service slot; status ENUM tracks lifecycle |
notifications |
In-app alerts delivered to users |
audit_log |
Immutable record of privileged admin actions |
| Role | Capabilities |
|---|---|
| Student | Browse services, view availability, create/cancel bookings, receive notifications |
| Staff (Provider) | Manage their own availability, approve or reject student bookings |
| Admin | Full access — manage all users, services, and view KPI reports |
After running npm run db:reset, the following accounts are available:
| Role | Password | |
|---|---|---|
| Admin | admin@university.edu |
password123 |
| Staff | staff1@university.edu |
password123 |
| Student | student1@university.edu |
password123 |
Additional staff and student accounts follow the same staff2–4 / student2–6 pattern.