A React and TypeScript task board backed by a C++ REST API, with Docker, GitHub Actions, and a live demo.
| Service | URL |
|---|---|
| App | https://task-tracker-web-4uqz.onrender.com |
| API | https://task-tracker-api-fak9.onrender.com · GET /api/tasks |
The frontend and API are two free Render services (render.yaml). Both run on Render's free tier and sleep when idle, so the first load can take about 15 seconds while the app and API wake up; after that it responds normally. Task data lives in the API container and is cleared when that service restarts or redeploys.
Local run of this frontend against the local API.
- Add a task with a description
- List tasks, and filter by todo, in progress, or done
- Change a task's status from the row
- Delete a task
- Save tasks in a JSON file on the API
- Reject bad input (
400) and unknown ids (404). A failed save returns500and leaves the previous file in place
| Layer | Choice |
|---|---|
| API | C++17, cpp-httplib, nlohmann/json |
| Frontend | React 19, TypeScript, Vite |
| Frontend tests | Vitest, Testing Library |
| API tests | GoogleTest, plus a Python HTTP regression script |
| Containers | Docker Compose locally, Docker on Render |
| CI | GitHub Actions (API, frontend, and a Compose smoke test) |
| Hosting | Render (Docker, render.yaml) |
The HTTP API grew out of the command-line app in Task-Tracker-CLI/ (also published as joelng25/Task-Tracker-CLI).
flowchart LR
UI["React UI"] -->|"JSON /api/tasks"| API["C++ API"]
API --> File["tasks.json"]
The browser never reads the file. It talks to four routes on the API:
| Method | Path | Body |
|---|---|---|
GET |
/api/tasks |
Optional ?status=todo|in-progress|done |
POST |
/api/tasks |
{ "description": "..." } |
PUT |
/api/tasks/:id |
{ "status": "todo" | "in-progress" | "done" } |
DELETE |
/api/tasks/:id |
— |
How the UI finds that API depends on where it is hosted:
- Local
npm run dev.VITE_API_URLdefaults tohttp://localhost:8080/api. The API allowshttp://localhost:3000viaCORS_ORIGIN. - Docker Compose. The image is built with
VITE_API_URL=http://localhost:8080/api. The API port is published on the host, and tasks are stored in a Docker volume. - Render. The frontend image defaults
VITE_API_URLto/api. nginx in that container proxies/api/toTASK_API_URL(the public API URL inrender.yaml), so the browser stays on one origin.
VITE_API_URL is read in task-tracker-web/src/services/taskService.ts. Vite inlines it at build time. A trailing slash is removed. See .env.example for the usual values. Do not put secrets in that variable or in git. The demo API URL is public.
docker compose up --buildApp: http://localhost:3000. API: http://localhost:8080.
Needs a C++17 compiler and make. Tests also need GoogleTest.
cd Task-Tracker-CLI
make
./task-server| Variable | Default | Role |
|---|---|---|
HOST |
localhost |
Bind address. The Docker image sets 0.0.0.0 |
PORT |
8080 |
Listen port |
CORS_ORIGIN |
http://localhost:3000 |
Allowed browser origin. Render sets * |
TASKS_FILE |
tasks.json |
Task file. The Docker image uses /data/tasks.json |
Needs Node.js 22 and npm.
cd task-tracker-web
npm install
npm run devhttp://localhost:3000. Point it at another API with VITE_API_URL (include /api).
CI runs on every push and pull request:
- API unit tests and the persistence test (
make run-testsinTask-Tracker-CLI/) - Frontend unit tests and
npm run build docker compose build, then a smoke test that posts a task and checks the web page
API regressions runs Task-Tracker-CLI/test_api.py against a local server.
# from the repo root
(cd Task-Tracker-CLI && make run-tests)
(cd Task-Tracker-CLI && make task-server && python3 test_api.py)
(cd task-tracker-web && npm test)test_api.py builds nothing itself. It starts ./task-server on a free port, then stops it.
The live demo is deployed on Render from render.yaml: in Render, New → Blueprint, pick this repo, and it creates the API and web services from their Dockerfiles. If the API URL changes, update TASK_API_URL in render.yaml.
Task-Tracker-CLI/ C++ API, CLI, and API tests
task-tracker-web/ React + TypeScript app
docs/screenshots/ README images
render.yaml Render blueprint (API + web)
docker-compose.yml Local API + web