A full-stack Todo application built as part of the AINE Program Training using BMAD methodology.
frontend/ React + Vite + TypeScript SPA (port 5173)
backend/ Node.js + Fastify + TypeScript REST API (port 3000)
└─ PostgreSQL 16 database (port 5432)
3-tier: React SPA → Fastify REST API → PostgreSQL
All services containerized and orchestrated via docker-compose.
- Docker ≥ 20.10
- Docker Compose ≥ 2.0 (typically bundled with Docker Desktop)
- Node.js ≥ 18 (LTS recommended)
- npm ≥ 9
- PostgreSQL ≥ 14 (local installation or Docker)
- Git (for cloning the repository)
Verify prerequisites:
docker --version # Docker version 20.10+
docker-compose --version # Docker Compose version 2.0+
node --version # v18+
npm --version # v9+
psql --version # psql 14+docker-compose upPrerequisites: Node.js ≥ 18, PostgreSQL running locally
# 1. Start the database (or ensure PG is running)
# 2. Copy and edit env
cp .env.example backend/.env
# 3. Run migration
cd backend && npm install && npm run migrate
# 4. Start backend
npm run dev # http://localhost:3000
# 5. In a new terminal — start frontend
cd ../frontend && npm install && npm run dev # http://localhost:5173cd frontend
npm test # Unit tests (Vitest)
npm run test:coverage # Unit tests with coverage
npm run test:e2e # Playwright E2E testscd backend
npm test # Unit/integration tests (Vitest)
npm run test:coverage # Tests with coverageProblem: ERROR: No such service: db or services fail to start
Solution:
# Stop all containers and remove volumes
docker-compose down -v
# Rebuild and restart
docker-compose up --buildProblem: Port already in use (5173, 3000, or 5432)
Solution:
# Find process using the port (macOS/Linux)
lsof -i :5173 # or :3000 or :5432
# Kill the process
kill -9 <PID>
# Or change the port in docker-compose.ymlProblem: Database connection refused
Solution:
# Check database health
docker-compose ps
# If db is unhealthy, check logs
docker-compose logs db
# Ensure healthcheck passes before API starts
# (already configured in docker-compose.yml)Problem: Migration fails with "relation already exists"
Solution:
# Migrations are idempotent (use CREATE TABLE IF NOT EXISTS)
# If you need to reset the database:
psql -U todo -d todos -c "DROP TABLE IF EXISTS todos CASCADE;"
npm run migrateProblem: CORS errors when frontend calls API
Solution:
Verify CORS_ORIGIN=http://localhost:5173 in backend .env file matches the frontend URL.
Problem: Frontend shows blank page
Solution:
# Check browser console for errors
# Common fix: clear npm cache and reinstall
cd frontend
rm -rf node_modules package-lock.json
npm install
npm run devProblem: Playwright tests fail with "Target closed"
Solution:
# Ensure app is running before E2E tests
docker-compose up -d
cd frontend
npx playwright test
# Or use headed mode for debugging
npx playwright test --headedProblem: Coverage threshold not met
Solution:
# Generate detailed coverage report
npm run test:coverage
# Open HTML report
open coverage/index.html # macOS
xdg-open coverage/index.html # Linux- Create a story file in
_bmad-output/implementation-artifacts/ - Write tests first (TDD approach):
- Frontend: Component tests with React Testing Library
- Backend: Endpoint tests with Vitest + supertest
- Implement the feature incrementally
- Run tests after each change:
npm test -- --watch # Auto-rerun on file change
- Update documentation (README, API contract, etc.)
# Run all checks before committing
cd frontend && npm run lint # ESLint
cd frontend && npm run type-check # TypeScript
cd backend && npm run lint
cd backend && npm run type-check
# Auto-fix linting issues
npm run lint -- --fix# Create a new migration file
touch backend/src/db/migrations/002_your_migration_name.sql
# Migrations run automatically on backend startup
# For local dev, run manually:
cd backend && npm run migrate# Via Docker Compose
docker-compose exec db psql -U todo -d todos
# Locally
psql -U todo -d todos -h localhost -p 5432
# Example queries
SELECT * FROM todos ORDER BY created_at DESC;
DELETE FROM todos WHERE completed = true;| Layer | Technology |
|---|---|
| Frontend | React 18 + Vite + TypeScript |
| State | TanStack Query (server state), React useState (local) |
| Styling | Tailwind CSS v3 |
| Routing | React Router v6 |
| Backend | Node.js + Fastify + TypeScript |
| Database | PostgreSQL 16 (raw SQL via pg) |
| Testing | Vitest + React Testing Library + Playwright |
| Infra | Docker Compose |
Base URL: http://localhost:3000/api
All responses use { data: ... } envelope for success and { error, message } for errors.
List all todos, ordered by creation date (newest first).
Request:
curl http://localhost:3000/api/todosResponse (200 OK):
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"description": "Buy groceries",
"completed": false,
"createdAt": "2026-03-14T10:00:00.000Z"
},
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"description": "Write documentation",
"completed": true,
"createdAt": "2026-03-13T15:30:00.000Z"
}
]
}Create a new todo.
Request:
curl -X POST http://localhost:3000/api/todos \
-H "Content-Type: application/json" \
-d '{"description": "Buy groceries"}'Response (201 Created):
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"description": "Buy groceries",
"completed": false,
"createdAt": "2026-03-14T10:00:00.000Z"
}
}Validation Error (422 Unprocessable Entity):
{
"error": "VALIDATION_ERROR",
"message": "description must be between 1 and 200 characters"
}Toggle a todo's completion status.
Request:
curl -X PATCH http://localhost:3000/api/todos/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Content-Type: application/json" \
-d '{"completed": true}'Response (200 OK):
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"description": "Buy groceries",
"completed": true,
"createdAt": "2026-03-14T10:00:00.000Z"
}
}Not Found (404):
{
"error": "NOT_FOUND",
"message": "Todo not found"
}Permanently delete a todo.
Request:
curl -X DELETE http://localhost:3000/api/todos/a1b2c3d4-e5f6-7890-abcd-ef1234567890Response (204 No Content):
(empty body)
Not Found (404):
{
"error": "NOT_FOUND",
"message": "Todo not found"
}Success responses always use the data envelope:
{ "data": <result> }Error responses use the error and message envelope:
{
"error": "ERROR_CODE",
"message": "Human-readable error description"
}Error codes:
VALIDATION_ERROR: Invalid input (422)NOT_FOUND: Resource not found (404)INTERNAL_ERROR: Unexpected server error (500)
This project was built using the BMAD (Business, Model, Architecture, Development) methodology with GitHub Copilot (powered by Claude Sonnet 4.5) as the AI assistant throughout all phases. All planning artifacts available in _bmad-output/planning-artifacts/.
The project followed a structured agent-based workflow:
- PM Agent (John): Requirements discovery and PRD creation
- Architect Agent (Winston): Technical decision support and architecture design
- SM Agent (Bob): Epic breakdown and story creation
- Dev Agent (Amelia): Story-driven implementation with TDD
- QA Agent (Quinn): Test strategy and quality gate validation
- Tech Writer Agent (Paige): Documentation and deliverables
AI Role: Requirements elicitation, user journey modeling, success criteria definition
Key Decisions:
- Identified zero-onboarding as primary value proposition
- Defined measurable quality gates: ≥70% coverage, ≥5 E2E tests, zero critical WCAG violations
- Scoped v1 to single-user with explicit architecture extensibility requirement for auth
Example Prompt:
"Generate a PRD for a Todo app targeting individual users with no authentication, focusing on session persistence and containerized deployment"
Outcome: prd.md with 9 FRs and 7 NFRs mapped to user journeys
AI Role: Framework evaluation, technology selection, API contract design
Key Decisions:
| Decision | Choice | AI Rationale |
|---|---|---|
| Frontend | React + Vite | Ecosystem maturity, axe-core/Playwright alignment |
| State Management | TanStack Query | Optimistic mutation lifecycle (onMutate/onError) for FR-06/FR-07 |
| Backend | Fastify + TypeScript | End-to-end TS; built-in schema validation for FR-02 |
| Database | PostgreSQL 16 | Production habits; Docker volume durability (NFR-07) |
| Data Access | Raw SQL (pg) |
Simplicity for single-table CRUD; educational value |
Example Prompt:
"Compare TanStack Query vs Zustand for managing optimistic UI updates with rollback on API failure"
Outcome: architecture.md with full Docker Compose topology and API contract
AI Role: Epic decomposition, acceptance criteria authoring, dependency sequencing
Key Decisions:
- Structured as 4 epics: Foundation → Read → Write → Quality
- Each story includes Gherkin-style ACs and explicit NFR coverage mapping
- Stories 3.4 and 3.5 specify optimistic update patterns with TanStack Query lifecycle hooks
Example Prompt:
"Break down Epic 3 into stories covering POST/PATCH/DELETE with optimistic updates and input validation"
Outcome: epics.md with 11 stories and 60+ acceptance criteria
AI Role: Test-first code generation, component scaffolding, migration authoring
Key Practices:
- All stories executed via Dev Story workflow with strict TDD
- Tests co-located with source files (
*.test.ts/*.test.tsx) - Integration tests use supertest against live Fastify server
- E2E tests use Playwright with axe-core accessibility validation
Example Prompt:
"Implement TodoItem component with toggle and delete handlers using TanStack Query mutations with optimistic updates and rollback"
AI-Generated Artifacts:
- Frontend:
TodoList,TodoItem,TodoForm,LoadingSpinner,ErrorMessagecomponents - Backend: Route handlers with Fastify schema validation
- Tests: 70%+ coverage across unit/integration/E2E
Implementation Stories:
| Metric | Value |
|---|---|
| Lines of AI-generated code | ~2,500 (frontend + backend + tests) |
| Test coverage achieved | 70%+ (target met via AI-generated tests) |
| Stories completed | 11/11 (100%) |
| Manual edits to AI code | < 10% (mostly config/env tweaks) |
| Critical AI hallucinations | 0 (all generated code validated via tests) |
Developer Reflection: AI accelerated development by ~3x compared to manual coding, with highest value in:
- Boilerplate generation (Docker configs, migration SQL, type definitions)
- Test scaffolding (Vitest + RTL + Playwright patterns)
- Requirement → AC → Code traceability (BMAD workflow automation)
See Framework Comparison for detailed evaluation of alternatives considered.
This project follows BMAD methodology with comprehensive planning artifacts and implementation records.
| Document | Description | Link |
|---|---|---|
| PRD | Product Requirements Document with 9 FRs, 7 NFRs, and user journeys | prd.md |
| Architecture | Technical decision document with API contract and system design | architecture.md |
| Epics | 4 epics decomposed into 11 stories with Gherkin-style acceptance criteria | epics.md |
| Implementation Readiness | Pre-implementation validation report | implementation-readiness-report-*.md |
| Story | Description | Link |
|---|---|---|
| Story 1.1-1.2 | Project Setup (monorepo, scaffolding, Docker Compose) | story-1.1-1.2-project-setup.md |
| Story 4.1-4.3 | Quality Gates (tests, E2E, accessibility) | story-4.1-4.3-quality-gates.md |
| Story 4.4 | README and Deliverables (this document) | story-4.4-readme-deliverables.md |
- Framework Comparison: docs/framework-comparison.md
- BMAD Methodology: .github/copilot-instructions.md
This project is part of the AINE Program Training and is provided for educational purposes.
Last Updated: 2026-03-14
Version: 1.0
BMAD Workflow: Complete (Analysis → PRD → Architecture → Implementation → Quality Gates → Delivery)