Internal tool the team uses to track its own work items: a REST API plus a small web interface. A task has a title, an optional description, a status, an optional assignee, an optional due date and a list of free-form tags. Tasks can be listed, filtered and sorted.
The service is deliberately small. There is no user management and no authentication — it only runs inside the internal network.
The web interface is plain HTML, CSS and JavaScript served straight from
static/. It uses ES modules, so there is no build step and no npm — editing
a file and reloading the browser is the whole development loop.
- Python 3.11 or newer
- uv for dependency and environment management
uv sync --extra devThis creates a virtual environment in .venv/ and installs runtime and
development dependencies.
uv run uvicorn taskbox.main:app --reloadThen open http://127.0.0.1:8000 for the web interface. The interactive API documentation is at http://127.0.0.1:8000/docs.
Using the service changes data/tasks.json. To get back to the state a fresh
clone starts with:
uv run reset-dataThis copies data/tasks.seed.json — the committed template — over the data
file. Try things out freely and reset whenever the data has drifted.
All four checks must pass before a change is merged. They are independent — a green test run says nothing about type errors, and vice versa.
uv run pytest # tests
uv run ruff check . # linting
uv run ruff format --check . # formatting
uv run mypy src tests # type checks
uv build # build (wheel + sdist into dist/)src/taskbox/ backend
main.py FastAPI application, router and static file registration
models.py Pydantic models (Task, TaskCreate, TaskUpdate, TaskStatus)
storage.py JSON persistence (TaskStore)
service.py business logic: create/update/delete, filtering, sorting
config.py data file and static directory locations
api/
dependencies.py shared FastAPI dependencies
tasks.py HTTP endpoints under /tasks
static/ web interface (no build step)
index.html page structure
css/style.css styling
js/api.js REST calls, the only place that knows about URLs
js/render.js builds the DOM for the task list
js/filters.js reads the filter form
js/taskForm.js reads the "new task" form
js/app.js entry point, wires events to the modules
tests/ pytest suite, mirrors the backend module layout
data/tasks.json task data the service reads and writes
data/tasks.seed.json template restored by `uv run reset-data`
docs/ architecture and API reference
Requests enter through the API layer (api/), which only translates between HTTP
and the service layer. All rules live in service.py. Persistence is isolated in
storage.py, so the storage format can be replaced without touching the layers
above. The web interface mirrors that split: api.js is the only frontend module
that talks HTTP, the rest works with plain task objects. See
docs/architecture.md for details.
- Business rules belong in
service.py, never inapi/. - The API layer raises
HTTPException; the service layer raises domain errors such asTaskNotFoundError. - Every code change comes with tests in
tests/, namedtest_<module>.py. - Public functions carry type annotations;
mypyruns in strict mode. - Line length is 100 characters (enforced by ruff).
data/tasks.jsonis committed on purpose so a fresh clone has sample data. Tests never write to it — they use a temporary file (seetests/conftest.py).data/tasks.seed.jsonis the template foruv run reset-dataand must stay byte-identical todata/tasks.jsonin a clean checkout. Both files use the exact formatting the service writes, so running the app never produces whitespace-only diffs.tests/test_seed_data.pyenforces this.
- No build step, no npm, no framework. Plain ES modules loaded by the browser.
js/api.jsis the only module that knows URLs and HTTP verbs. Other modules never callfetchdirectly.- Task values are written to the DOM with
textContent, neverinnerHTML, so user input cannot inject markup. - After a change the UI re-reads the list from the server instead of patching its own copy of a task.
- A new feature usually touches the whole chain:
models.pyandservice.pyin the backend, thenapi.js,render.jsand the relevant form module in the frontend.
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
Liveness probe |
| GET | /tasks |
List tasks, with filters and sorting |
| POST | /tasks |
Create a task |
| GET | /tasks/{id} |
Fetch a single task |
| PATCH | /tasks/{id} |
Update selected fields of a task |
| DELETE | /tasks/{id} |
Delete a task |
Full parameter documentation: docs/api.md.