My Reading Tracker is the project's stable internal identity. It is a private family website for recording children's reading activity, with a separately configurable user-facing product name. The first release should make it quick for a parent to:
- Sign in.
- Create a child reader profile.
- Find or manually add a book.
- Add the book to the child's library.
- Record reading minutes and pages.
- Review recent activity and progress totals.
The architecture should support a future mobile client without requiring the backend or database to be rewritten.
- Parent authentication
- A household owned by the parent
- Multiple child reader profiles
- Book search by title, author, or ISBN
- Google Books as the primary metadata source
- Open Library as a fallback source
- Editable imported metadata
- Manual book entry
- Per-reader book status: planned, reading, or finished
- Reading-session logging
- Recent reading history
- Weekly and monthly totals
- Edit and delete operations
- Basic JSON or CSV data export
- Desktop-first website with reasonable small-screen behavior
- Native mobile application
- Barcode camera scanning
- Offline support
- Goals, streaks, badges, or leaderboards
- Teacher and school reports
- Reviews, recommendations, or social features
- AI-generated content
- Payments or subscriptions
- React
- TypeScript
- Vite
- React Router
- Tailwind CSS
- shadcn/ui
- React Hook Form
- Zod
- TanStack Query for server-state fetching and caching
- Vitest and React Testing Library
- Playwright for end-to-end tests
- Python
- FastAPI
- Pydantic
- SQLAlchemy
- Alembic database migrations
- PostgreSQL driver with connection pooling
- HTTPX for Google Books and Open Library requests
- pytest
- Supabase-hosted PostgreSQL
- Supabase Auth
- Vercel for the static frontend
- Render or another container host for FastAPI
- Google Books API
- Open Library API
- Git and GitHub
- Docker for repeatable backend development and deployment
- Ruff for Python linting and formatting
- mypy or Pyright for Python type checking
- ESLint and Prettier for TypeScript
- OpenAPI-generated TypeScript API client
Exact dependency versions will be pinned when each application is scaffolded.
React website
|
| HTTPS /api/v1 + Supabase access token
v
FastAPI backend
|-- verifies authentication and household access
|-- implements reading and book business rules
|-- calls Google Books and Open Library
|-- produces an OpenAPI contract
v
Supabase-hosted PostgreSQL
Future React Native app ------> same FastAPI backend
The frontend must never receive the PostgreSQL password or Google Books API key. All database mutations and external book searches pass through FastAPI.
my-reading-tracker/
|-- frontend/
| |-- src/
| | |-- api/
| | |-- components/
| | |-- features/
| | | |-- auth/
| | | |-- books/
| | | |-- dashboard/
| | | |-- readers/
| | | `-- sessions/
| | |-- layouts/
| | |-- routes/
| | |-- schemas/
| | `-- test/
| `-- package.json
|-- backend/
| |-- app/
| | |-- api/v1/
| | |-- core/
| | |-- database/
| | |-- integrations/
| | |-- models/
| | |-- repositories/
| | |-- schemas/
| | `-- services/
| |-- migrations/
| |-- tests/
| `-- pyproject.toml
|-- docs/
|-- .env.example
|-- docker-compose.yml
|-- Makefile
`-- README.md
Organize code by feature in the frontend and by responsibility in the backend. The backend remains a modular monolith; microservices are unnecessary.
id: UUID primary keyname: textcreated_at: timestamp with time zoneupdated_at: timestamp with time zone
household_id: foreign key to householdsuser_id: Supabase Auth user UUIDrole: owner or readercreated_at: timestamp with time zone- Unique constraint on household and user
id: UUID primary keyhousehold_id: foreign key to householdsname: textavatar_key: nullable textcreated_at: timestamp with time zoneupdated_at: timestamp with time zone
Children are reader profiles and do not have login accounts.
id: UUID primary keyhousehold_id: foreign key to householdstitle: textsubtitle: nullable textauthors: text arrayisbn_10: nullable textisbn_13: nullable textcover_url: nullable textpublisher: nullable textpublished_date: nullable textpage_count: nullable positive integerdescription: nullable textlanguage: nullable textmetadata_source: manual, google_books, or open_libraryexternal_source_id: nullable textcreated_at: timestamp with time zoneupdated_at: timestamp with time zone
Book metadata is copied into My Reading Tracker rather than fetched every time it is displayed. Imported fields remain editable.
id: UUID primary keyreader_id: foreign key to readersbook_id: foreign key to booksstatus: planned, reading, or finishedstarted_at: nullable datefinished_at: nullable datecreated_at: timestamp with time zoneupdated_at: timestamp with time zone- Unique constraint on reader and book
id: UUID primary keyreader_id: foreign key to readersbook_id: foreign key to bookssession_date: dateminutes: positive integerstart_page: nullable non-negative integerend_page: nullable non-negative integeractivity_type: independent, with_adult, read_aloud, or audiobooknotes: nullable textfinished_book: booleancreated_at: timestamp with time zoneupdated_at: timestamp with time zone
Weekly and monthly totals are derived from reading sessions. They are not stored as duplicate counters in version one.
All endpoints are under /api/v1.
GET /healthGET /me
GET /readersPOST /readersGET /readers/{reader_id}PATCH /readers/{reader_id}DELETE /readers/{reader_id}
GET /book-search?q=...GET /book-search/isbn/{isbn}GET /booksPOST /booksGET /books/{book_id}PATCH /books/{book_id}DELETE /books/{book_id}POST /readers/{reader_id}/booksPATCH /readers/{reader_id}/books/{book_id}DELETE /readers/{reader_id}/books/{book_id}
GET /reading-sessionsPOST /reading-sessionsGET /reading-sessions/{session_id}PATCH /reading-sessions/{session_id}DELETE /reading-sessions/{session_id}GET /reports/summaryGET /reports/calendarGET /exports/reading-data
Every household-scoped endpoint must resolve the authenticated user's household and reject access to records outside it.
Tasks:
- Add the root README and development conventions.
- Create
frontend,backend, anddocsdirectories. - Add
.gitignore,.editorconfig, and.env.example. - Add root commands for setup, linting, tests, and local development.
- Decide whether local PostgreSQL will run in Docker or use a Supabase development project directly. Prefer Docker locally for repeatability.
Exit criteria:
- A new developer can identify the frontend, backend, and required services.
- No secrets are committed.
Tasks:
- Create the FastAPI application and
/healthendpoint. - Add settings loaded from environment variables.
- Configure structured logging and development CORS.
- Configure SQLAlchemy sessions.
- Configure Alembic.
- Add Ruff, type checking, and pytest.
- Add a Dockerfile and local development command.
Exit criteria:
- FastAPI starts locally.
/healthreturns a successful response.- Backend linting and tests pass.
Tasks:
- Create the React/Vite TypeScript application.
- Configure Tailwind CSS and shadcn/ui.
- Configure React Router and TanStack Query.
- Add the application shell, sidebar, header, and error boundary.
- Add ESLint, Prettier, Vitest, and Playwright.
- Add environment-based API configuration.
Exit criteria:
- The website starts locally.
- A placeholder dashboard renders through the application shell.
- Frontend linting and tests pass.
Tasks:
- Implement SQLAlchemy models for all initial tables.
- Create the first Alembic migration.
- Add foreign keys, unique constraints, and check constraints.
- Add timestamps and UUID generation consistently.
- Add repository-level integration tests using a test database.
Exit criteria:
- A new database can be built entirely from migrations.
- Invalid page counts, minutes, roles, and statuses are rejected.
Tasks:
- Configure Supabase Auth for email/password or magic-link login.
- Add frontend sign-in, sign-out, and protected routes.
- Send the Supabase access token to FastAPI as a bearer token.
- Validate tokens in FastAPI using Supabase signing keys.
- Create a household and owner membership for a first-time user.
- Add reusable household-authorization dependencies.
- Test missing, expired, and unauthorized tokens.
Exit criteria:
- An authenticated parent can enter the application.
- An unauthenticated visitor cannot access private routes or data.
- Cross-household record access is rejected.
Tasks:
- Implement reader CRUD services and endpoints.
- Add Pydantic validation for reader names.
- Generate or update the TypeScript API client from OpenAPI.
- Build reader list, create, edit, and delete interfaces.
- Add a selected-reader control used by later screens.
- Require confirmation before deleting a reader with history.
Exit criteria:
- A parent can create and maintain multiple child profiles.
- Reader data is isolated by household.
Tasks:
- Define an internal
BookProviderinterface. - Implement Google Books title, author, and ISBN search.
- Normalize external responses into one internal result schema.
- Implement Open Library fallback search and cover lookup.
- Add timeouts, retry limits, error handling, and result caching.
- Prevent API keys and raw provider errors from reaching the browser.
- Add mocked integration tests.
Exit criteria:
- A query produces normalized book choices with available covers.
- A provider failure produces a useful fallback or recoverable error.
Tasks:
- Implement book and reader-book services and endpoints.
- Build the book-search results interface.
- Build an editable confirmation form for imported metadata.
- Build manual book entry.
- Add books to a selected reader as planned, reading, or finished.
- Build library filters and book-detail pages.
- Define safe deletion behavior for books with reading sessions.
Exit criteria:
- A parent can search, select, correct, and save a book.
- Manual entry works when both external providers fail.
- The same saved book can belong to more than one reader.
Tasks:
- Implement session CRUD services and endpoints.
- Enforce minutes and page-range rules in the backend.
- Build a quick-log form optimized for recent books.
- Support all four activity types.
- Update the reader-book status when a session finishes a book.
- Build session edit and delete flows.
- Ensure repeated readings create distinct sessions.
Exit criteria:
- A normal reading session can be recorded in under 15 seconds.
- Incorrect entries can be edited or deleted.
- Finishing a book updates its library status consistently.
Tasks:
- Implement summary queries by reader and date range.
- Calculate minutes, pages, books finished, and reading days.
- Build weekly and monthly summary cards.
- Build current-book progress displays.
- Build recent activity and full history views.
- Add reader, book, date, and activity-type filters.
- Add loading, empty, and error states.
Exit criteria:
- Totals reconcile with saved sessions.
- The parent can understand recent progress from the dashboard.
Tasks:
- Add a complete JSON export for backup.
- Add a human-readable CSV session export.
- Add provider-response caching where appropriate.
- Add retry and timeout behavior for network operations.
- Add user-friendly handling for paused or unavailable free services.
Exit criteria:
- The parent can download all important reading data.
- Temporary book-provider failure does not affect saved books.
Status: implemented on 2026-08-14. Automated checks and the review record live
in .github/workflows/ci.yml, frontend/e2e, backend/scripts/run_e2e.py, and
docs/security-review.md.
Tasks:
- Unit-test calculations, validation, and metadata normalization.
- Integration-test repositories and API endpoints.
- End-to-end test the primary parent workflow.
- Test authorization against cross-household identifiers.
- Review secret handling, CORS, logging, and error responses.
- Check keyboard navigation, labels, contrast, and focus behavior.
- Test current Chrome, Firefox, Edge, and Safari.
Required end-to-end workflow:
Sign in -> create reader -> find book -> add book -> log session
-> view dashboard -> edit session -> export data
Exit criteria:
- All automated checks pass in a clean environment.
- No known high-severity security or data-loss issue remains.
Status: repository deployment configuration and operating procedures were implemented on 2026-08-15. Creating the production Supabase, Render, and Vercel resources and completing production smoke tests require the account-specific URLs and credentials.
Tasks:
- Create separate Supabase development and production projects if feasible.
- Deploy FastAPI to Render or the selected backend host.
- Run production database migrations through a controlled command.
- Deploy the frontend to Vercel.
- Configure the frontend API URL and permitted origins.
- Configure Supabase authentication redirect URLs.
- Run production smoke tests.
- Document deployment, rollback, migration, and export procedures.
Exit criteria:
- The private website works from its production URL.
- Authentication, book search, logging, reporting, and export succeed.
Version one is complete only when a parent can:
- Sign in securely.
- Create at least one child reader.
- Search for a book by title, author, or ISBN.
- Correct imported metadata or enter a book manually.
- Add a book to a child's library.
- Record minutes and optional page progress.
- Mark a book finished.
- Review weekly and monthly activity.
- Edit or delete an accidental entry.
- Export the family's reading data.
Potential additions should be prioritized from real usage rather than built in advance:
- Reading goals and noncompetitive celebrations
- Barcode scanning
- Printable teacher reports
- Custom cover uploads
- Offline-capable reading logging
- React Native mobile client using the existing API
- Notifications and reminders
For each implementation step:
- Confirm its acceptance criteria.
- Implement the smallest vertical slice.
- Add or update automated tests.
- Run formatting, linting, type checking, and tests.
- Update API documentation and generated clients.
- Commit the completed step separately.
Do not begin post-version-one features until the primary logging workflow is deployed and used successfully.
The production application uses two server-assigned account roles:
owner: the first parent or teacher who creates a household or classroom; retains full administrative access.reader: a child invited by an administrator and linked to exactly one reader profile; can use only that reader's library, logs, rewards, and reports.
The public account-type choice explains these paths but never grants a role. Role assignment remains a backend responsibility: new household setup creates the single owner, and reader access requires an invitation. A reader may not self-select administrator access.
Status: implemented on 2026-08-24 and revised on 2026-08-25. The existing FastAPI authorization rules enforce one owner administrator and reader-profile isolation. The role rules above are the contract for the remaining authentication redesign.
Status: implemented on 2026-08-24. The sign-in screen now introduces separate Parent or teacher and Reader paths, describes their capabilities, and informs readers that an administrator invitation is required. The existing magic-link form remained available at this stage and was replaced by password authentication in Step 3.
Status: implemented on 2026-08-25. Parents and teachers can create an account
with their name, family or classroom name, email, and password. Email
confirmation is disabled, so a successful registration signs the user in
immediately. Existing users sign in with email and password, and Supabase
persists the browser session. Email is reserved for the forgot-password flow,
which returns through /reset-password to choose a new password. Registration
metadata may name the initial household, but it never assigns an application
role; FastAPI remains responsible for owner and reader membership. Reader
account creation and invitation delivery remain part of Step 4.
Status: implemented on 2026-08-25 and revised on 2026-08-26. The owner can invite a reader using only an email address. FastAPI records the invitation and uses the Supabase Auth admin API to send a real activation email. The reader opens its link, enters their name, and creates a password. On first access, FastAPI creates the reader profile card, links it to the login, and assigns the server-controlled reader role. The separate Add reader flow remains available for children who share the owner’s login. A reader activation without a matching invitation is rejected instead of creating a new owner household. Removing pending access cancels it; removing accepted access revokes the linked reader membership without deleting reading data. Each household has only one adult management login: its owner. The Readers page presents invitations and active logins in a dedicated Manage reader access section. Explicit Cancel invitation and Revoke access actions replace ambiguous removable badges, and confirmation dialogs explain their effect before access changes.
Status: implemented on 2026-08-25. Expected authorization failures now have specific user guidance: an uninvited or revoked reader is told to contact the owner, while an expired backend session is cleared and returned to normal sign in. The authenticated Account page displays email, role, household, and linked reader access. Owners and readers can change their password from an active session without sending email; email remains reserved for forgotten-password recovery. Automated tests cover account details, password changes, invitation errors, and expired-session handling.