CaseFlow is a full-stack case-management workflow built to demonstrate a complete React-to-PostgreSQL product slice. Operators can create and assign cases, move work through a defined status lifecycle, record activity, and see database-backed queue totals.
- Create a case for an existing user; the server owns the initial
OPENstatus. - Review case details and move work through open, in-progress, resolved, and closed states.
- Reassign a case while keeping list and detail views synchronized.
- Add persistent activity notes and display the newest activity first.
- Calculate total and per-status workflow counts in PostgreSQL.
- Handle loading, empty, validation, error, retry, saving, and success states in the interface.
| Case workflow | Responsive case intake |
|---|---|
![]() |
![]() |
Open the live CaseFlow application
Watch the 22-second workflow demonstration: create and assign a case, update its status, reassign ownership, add an activity note, and return to the queue to see the database-backed summary refresh. A higher-quality MP4 version is also available to download.
flowchart LR
Operator[Operator] --> Router[React Router pages]
subgraph Frontend[React + TypeScript frontend]
Router --> Query[TanStack Query]
Query --> Client[Typed native fetch client]
end
Client -->|JSON over /api| Controllers[Spring MVC controllers]
subgraph Backend[Spring Boot backend]
Controllers --> Services[Transactional services<br/>Bean Validation]
Services --> Repositories[Spring Data JPA<br/>entity graphs and aggregation]
end
Repositories --> Database[(PostgreSQL)]
Flyway[Flyway migrations] --> Database
The repository intentionally remains one frontend, one backend, and one PostgreSQL service. API records keep persistence entities out of the wire contract, service methods define transaction boundaries, and Flyway owns the database schema.
The live application runs as three Railway services:
- Caddy serves the Vite production build, provides React Router SPA fallback,
and proxies
/apiover Railway's private network. - Spring Boot runs in a Java 25 container and exposes an Actuator health check.
- Railway PostgreSQL stores application data, with Flyway applying schema migrations during backend startup.
Only the frontend has a public domain. The backend and database remain private, so browser requests stay same-origin and no database credentials are exposed to the client.
TanStack Query keeps related views consistent after mutations:
- Status and assignee updates replace the current detail cache and invalidate the case list.
- Status changes also invalidate the workflow summary.
- Case creation seeds the new detail cache, invalidates the list and summary, and then navigates to the new case.
- New activity is inserted at the beginning of the cached activity timeline.
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/api/cases |
List cases and their assigned users |
POST |
/api/cases |
Validate and create an open case |
GET |
/api/cases/summary |
Return total and per-status counts |
GET |
/api/cases/{id} |
Return one case or 404 |
PATCH |
/api/cases/{id}/status |
Validate and persist a status change |
PATCH |
/api/cases/{id}/assignee |
Reassign a case to an existing user |
GET |
/api/cases/{id}/activities |
List activity newest first |
POST |
/api/cases/{id}/activities |
Validate and persist an activity note |
GET |
/api/users |
List users available for assignment |
| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript 6, Vite, React Router, TanStack Query, native fetch() |
| Backend | Java 25, Spring Boot 4, Spring Web MVC, Spring Data JPA, Bean Validation |
| Data | PostgreSQL 18, Flyway, Docker Compose |
| Deployment | Railway, Docker, Caddy, private service networking |
| Verification | JUnit, Mockito, MockMvc, Oxlint, TypeScript compiler, Vite production build |
- Java 25
- Maven 3.9+
- Node.js 24 LTS
- npm 11+
- Docker and Docker Compose
PostgreSQL runs through Docker Compose; no local PostgreSQL installation is required.
From the repository root:
docker compose up -d --wait postgresIn a second terminal:
cd backend
mvn spring-boot:runFlyway creates and seeds the schema on the first run. The API listens on
http://localhost:8080.
In a third terminal:
cd frontend
npm ci
npm run devOpen http://localhost:5173/cases. During local
development, Vite proxies /api requests to the Spring Boot service.
docker compose downThis stops the container without deleting the named database volume.
Run the automated checks from their respective project directories:
cd backend
mvn testcd frontend
npm ci
npm run lint
npm run buildA local and live deployment verification on 3 September 2026 confirmed:
- 25 backend tests pass with no failures or errors.
- Frontend dependency installation, lint, TypeScript compilation, and the Vite production build pass.
- PostgreSQL 18.6 starts through Docker Compose and Flyway applies both migrations to an empty schema.
- The browser flow creates a case, changes its status and assignee, adds an activity, preserves all three changes after reload, and refreshes the case list and workflow summary.
- The list, details, and intake views remain readable at desktop and mobile breakpoints.
- The Railway deployment passes frontend and backend health checks, serves nested routes after a direct page refresh, and keeps the backend and database on private networking.
The backend suite uses focused MockMvc and service tests with mocked repositories. The frontend currently relies on lint, production build, and manual browser verification rather than an automated browser-test dependency.
CaseFlow was designed as a deliberately scoped portfolio project. Authentication, search, pagination, and broader commercial-product concerns were excluded so the implementation could remain focused on a complete, inspectable full-stack workflow.
caseflow/
├── backend/ # Spring Boot API, tests, and Flyway migrations
├── frontend/ # React client and production Caddy configuration
├── docs/ # Portfolio screenshots and demo media
├── docker-compose.yml # Local PostgreSQL service
└── README.md



