A versioned full-stack reading tracker built to demonstrate modern frontend architecture, behavioral parity testing, and CI-gated development.
Try the deployed full-stack app here:
βΆ https://readr-v2-app.vercel.app
The demo is now connected to the Express + PostgreSQL backend, with all reading data persisted via the API.
- Data is fully server-backed (v2.2+)
- Changes persist across sessions and devices
- Authentication + user-scoped data (v2.3 in progress)
- Backup export/import now implemented with ownership enforcement
v2.3 introduces secure multi-user data boundaries and safe bulk import.
Readr v2 is a full-stack reading tracker designed to demonstrate modern frontend architecture, API-driven persistence, and disciplined system evolution.
It is a structured rewrite of the original offline-first app (v1.0βv1.9), transitioning to a scalable, multi-user system.
Key goals:
- Build a React + TypeScript frontend with strict behavioral parity guarantees
- Introduce a typed Express + PostgreSQL backend
- Enforce clear separation between UI, state, and persistence
- Validate correctness through CI and full-stack testing
Each version isolates a specific risk area (parity, persistence, ownership) before introducing new complexity.
The original v1.x app remains available here: βΆ https://github.com/conorgregson/reading-log-app
Current focus: v2.3 β Authentication & data ownership
Readr v2 was designed to demonstrate several real-world frontend engineering patterns:
-
Behavioral Parity Testing The React frontend rebuild enforces v1.9 behavioral parity using automated tests to prevent regressions during architectural migration.
-
Deterministic UI State Session history sorting is guaranteed deterministic so identical datasets always produce identical ordering.
-
Undo Architecture Critical actions (delete / finish) support ~6s undo windows while preserving filters, search state, and list ordering.
-
Local-First β API Migration Strategy Readr evolved through a staged migration to reduce system-wide risk:
- v1.x: fully offline-first (localStorage)
- v2.1: React rebuild maintained local persistence for parity lock
- v2.2: full migration to API-backed persistence (Express + PostgreSQL)
This approach ensured UI behavior remained stable while replacing the underlying data layer.
-
CI-Gated Development GitHub Actions enforces typecheck, lint, and test validation on every push and pull request.
These patterns mirror practices used in production applications where architectural changes must not introduce behavioral regressions.
Readr v2.3 introduces strict per-user data boundaries across the system.
Key guarantees:
- All data is scoped to the authenticated user
- Backup export returns only user-owned records
- Backup import enforces ownership (incoming
userIdis ignored) - Invalid relationships (e.g., orphan sessions) are rejected
- Failed imports rollback completely (no partial writes)
These constraints are enforced at both the API layer and database level, and validated through integration testing.
This ensures the system is safe for multi-user environments.
v2.1 goal: rebuild the v1.9 frontend in React + TypeScript with behavior parity before any API migration.
Tier 0 Lock (freeze gates):
- Books/Search locked (Sprint 5): Undo (~6s), highlight parity, autocomplete parity, regression tests
- Sessions locked (Sprint 7): CRUD + deterministic sorting, keyboard navigation + live regions, Undo (~6s), highlight parity, regression tests
Hardening (Sprint 8):
- Accessibility + focus management baseline
- Corrupt storage resilience
- Performance sanity check on large libraries
CI baseline (Sprint 9):
- Typecheck + tests required on PRs
- βIntentional regressionβ proof test to confirm the suite catches breakages
Canonical docs:
- Parity Charter:
docs/sprints/v2.1/parity-charter-v2.1.md - Architecture:
docs/sprints/v2.1/architecture-v2.1.md - Test Matrix:
docs/sprints/v2.1/test-matrix-parity.md - Dependency Map:
docs/sprints/v2.1/dependency-map-v2.1.md
- Overview
- Parity Summary (v2.1)
- Why This Project
- Roadmap Philosophy
- Changelog
- Release Strategy
- Roadmap (High-Level)
- Tech Stack
- Project Structure
- Architecture
- Engineering Decisions
- Screenshots
- Installation & Development
- Author
- License
Readr is both a product and a systems-design exercise.
It demonstrates:
- Incremental, versioned system evolution
- Safe migration from local-first β API-backed architecture
- Strict separation of concerns across frontend and backend
- Schema-driven validation (Zod + Prisma)
- Full-stack testing (UI parity + API integration)
The goal is not just to build features, but to evolve architecture intentionally while maintaining correctness at every step.
Readr is developed in versioned milestones where each release isolates a specific risk area (e.g., architecture, persistence, UX, or scale) before introducing new complexity.
The roadmap documents not just what was built, but why β serving as both a planning tool and a technical narrative.
See the full roadmap in roadmap.md.
All notable changes are documented in CHANGELOG.md,
following Keep a Changelog and Semantic Versioning.
Readr uses two parallel versioning systems:
Major milestones follow SemVer and represent stable, coherent deliverables:
v2.0.0β Backend & CI foundationv2.1.0β React frontend rebuild- Future versions increment semantically
These releases are published in GitHub Releases.
During active development, sprint tags are used to mark internal milestones:
v2.1-sprint-0v2.1-sprint-1- β¦
v2.1-sprint-9
Sprint tags serve as:
- Structured iteration checkpoints
- Rollback anchors
- Evidence of disciplined development cadence
Only SemVer releases represent official βship-readyβ states.
- v2.0.0 β Backend & CI foundation (Express + Prisma + PostgreSQL) β
- v2.1.0 β React frontend rebuild with full v1.9 behavioral parity β
- v2.2.0 β API integration & persistence migration (local-first β API) β
- v2.3.0 β Authentication, accounts, and multi-user data boundaries π§
- v2.4.0 β Server-driven badges, statistics, and engagement systems
- v3.0.0 β Production infrastructure & hosted deployment architecture
For detailed version history and architectural milestones, see roadmap.md.
- React 18
- TypeScript (strict mode)
- Vite
- Tailwind CSS
- React Router
- Zustand
- Vitest + React Testing Library
- Node.js + TypeScript
- Express
- Prisma ORM
- PostgreSQL
- Zod
- Docker
- GitHub Actions
Readr includes both frontend parity tests and backend integration tests to ensure system-wide correctness.
The React rebuild enforces strict behavioral parity with v1.9.
Covered areas:
- Search engine logic (tokenization, fuzzy matching, AND semantics)
- Books undo system (delete/restore integrity)
- Sessions sorting (deterministic ordering guarantees)
- Keyboard navigation and accessibility behavior
Tools:
- Vitest
- React Testing Library
- jsdom
These tests ensure that architectural changes do not introduce UI regressions.
The backend includes API-level integration tests to validate correctness, security, and data integrity.
Covered areas:
-
Authentication
- Register / login flows
- Protected route enforcement (
401on unauthorized access)
-
Backup Export
- Returns only authenticated user data
- Prevents cross-user data leakage
-
Backup Import
- Valid payload ingestion
- Duplicate ID rejection
- Orphan relationship validation (sessions β books)
- Transaction rollback on failure
- Forced ownership assignment (never trusts incoming
userId)
Tools:
- Vitest
- Supertest
- PostgreSQL test database (
readr_v2_test)
These tests validate that the system enforces strict per-user data boundaries, a core requirement of v2.3.
In addition to automated tests, the API is validated using structured Postman collections:
Health/Auth/Backup/Books/Sessions/
These collections support:
- Manual verification of endpoints
- Regression testing during development
- Real-world API interaction simulation
GitHub Actions runs automated validation on every push and pull request.
Pipeline steps:
- Type checking
- ESLint validation
- Test suite execution (frontend + backend)
A regression-proof validation was performed by intentionally introducing failures to confirm CI blocks broken builds.
This layered testing strategy ensures:
- UI behavior remains stable (parity protection)
- API contracts remain correct
- Data integrity is enforced across users
- Security boundaries cannot be bypassed
This mirrors production-grade systems where frontend, backend, and data ownership must all be validated independently.
The repository is organized by architectural responsibility rather than framework convention, reinforcing separation between presentation, business logic, and persistence layers.
readr-v2/
β
βββ client/ # React frontend
β βββ scripts/
β βββ src/
β β βββ app/ # App shell + router
β β βββ features/ # Feature domains (books/sessions/settings)
β β βββ shared/ # UI primitives + shared utilities
β β βββ test/
β β βββ index.css
β β βββ main.tsx
β βββ index.html
β βββ vite.config.ts
β βββ vitest.config.ts
β βββ vercel.json
β
βββ server/ # Express backend
β βββ src/
β β βββ api/ # Route definitions
β β βββ services/ # Business logic
β β βββ schemas/ # Zod validation schemas
β β βββ db/ # Prisma client and DB helpers
β β βββ index.ts # Server entry point
β βββ prisma/
β β βββ schema.prisma
β βββ docker-compose.yml
β
βββ docs/
β βββ sprints/ # v2.1 sprint blueprints
β β βββ README.md # v2.1 sprints README
β βββ v2.1-parity-charter.md
β βββ v2.1-architecture.md
β βββ v2.1-test-matrix.md
β βββ v2.1-dependency-map.md
β βββ v2.2-api-integration-blueprint.md
β βββ v2.3-feature-expansion-blueprint.md
β
βββ CHANGELOG.md
βββ roadmap.md
βββ LICENSE.md
βββ README.md ββββββββββββββββββββββββββββ
β React UI β
β (Vite + TS + Tailwind) β
βββββββββββββββ¬βββββββββββββ
β
βΌ
Client Services Layer
API-backed persistence (v2.2)
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Express API β
β Node.js + TypeScript + Zod β
βββββββββββββββββ¬ββββββββββββββββββ
β
βΌ
Business Logic Layer
(services/, controllers/)
β
βΌ
βββββββββββββββββββββββββ
β Prisma ORM β
β (Typed DB access) β
βββββββββββββ¬βββββββββββ
β
βΌ
βββββββββββββββββββββββββββ
β PostgreSQL DB β
β Dockerized Local Dev β
βββββββββββββββββββββββββββReadr v2 is deployed as a split frontend/backend architecture:
- Frontend: Vercel (React + Vite)
- Backend: Render (Express API)
- Database: PostgreSQL (Neon / Docker for local)
The system relies on strict environment configuration for production:
VITE_API_BASE_URL- Points to the deployed backend API
- Example:
https://readr-impd.onrender.com
DATABASE_URLJWT_SECRET
These variables are required at runtime and must be configured in the deployment platform (not just .env locally).
During production rollout, several issues were identified and resolved:
-
Environment variable mismatch
VITE_API_BASE_URLwas misnamed, causing the frontend to fail API calls
-
Missing backend secret
JWT_SECRETwas not configured in Render, causing authentication failures (500errors)
-
Local vs production parity
- Local
.envvalues do not carry over to Vercel/Render automatically - Each platform requires explicit configuration
- Local
These fixes reinforced critical full-stack principles:
- Production systems depend on correct environment configuration
- Authentication systems require secure and consistent secrets
- Deployment platforms are isolated environments, not extensions of local dev
This mirrors real-world debugging scenarios where infrastructureβnot codeβis often the source of failure.
flowchart TD
A["React Frontend (Vite + TypeScript + Tailwind)"]
--> B["Client Services Layer (API-backed)"]
B --> C["Express Server (Node + TypeScript)"]
C --> D["Controller Layer"]
D --> E["Service Layer"]
E --> F["Prisma ORM"]
F --> G[("PostgreSQL Database")]
classDef teal fill:#008080,stroke:#004d4d,color:white;
classDef navy fill:#003366,stroke:#001933,color:white;
class A,B teal
class C,D,E navy
class F,G teal
Readr v2 emphasizes architectural clarity and incremental evolution over rapid feature expansion.
Key decisions:
The backend was built and stabilized before rewriting the frontend to:
- De-risk persistence and schema design early
- Lock API boundaries before UI coupling
- Establish CI-backed integration testing from the start
The React frontend rebuild prioritizes feature parity with v1.9 before introducing API-backed persistence. This avoids mixing behavioral changes with architectural migration.
- v1.x: fully offline-first
- v2.1: React rebuild stays local-first (parity lock)
- v2.2: migrate persistence to API (stable UI)
This staged migration reduces system-wide risk and simplifies debugging.
- UI components are isolated from state logic.
- Stores isolate state from persistence.
- Services abstract IO (local now, API later).
- Backend separates controllers, services, and schemas.
This keeps React β API integration friction low.
Backend endpoints are validated via automated API tests. v2.1 expands regression protection with parity tests and CI gating.
Engineering choices are documented to emphasize maintainability and long-term scalability.
The UI below reflects the current full-stack system with API-backed persistence, authentication, and user-scoped data.
All screenshots reflect the live application connected to the production API.
User authentication (login / account access)
Library view with search, filtering, and status tracking
Search with fuzzy matching and highlight rendering
Undo system (~6s window) preserving state, filters, and ordering
Session history with deterministic sorting and reading progress tracking
Add Book flow with structured input and validation-ready form
Backup export/import system with ownership-safe data handling
Responsive mobile layout
git clone https://github.com/conorgregson/readr-v2.git
cd readr-v2cd client
npm install
npm run devcd server
npm install
npm run devdocker-compose up -dBuilt and maintained by Conor Gregson.
- GitHub: https://github.com/conorgregson
- LinkedIn: https://www.linkedin.com/in/conorgregson
This project is licensed under:
Creative Commons AttributionβNonCommercial 4.0 International (CC BY-NC 4.0)
You may view, use, and modify the source code for non-commercial purposes only. Commercial use requires prior written permission.
Full license text: https://creativecommons.org/licenses/by-nc/4.0/legalcode
See the LICENSE file for details







