5.0 KiB
API Standards
These are the conventions for the Flask backend consumed by the
Vue frontend. Generic REST guidance is secondary to the patterns
already established in backend/app/api/.
Philosophy
- The frontend is the only consumer; we optimize for that contract, not for a hypothetical public API.
- Long-running work returns immediately with a
task_id; clients poll. There are no streaming responses or websockets. - The backend is stateless across restarts of
Project/Taskdata (in-memory), with deterministic recovery on boot.
URL Pattern
Routes live under /api/<domain>/<action> where domain matches the
Flask blueprint:
/api/graph/...—graph_bp(api/graph.py)/api/simulation/...—simulation_bp(api/simulation.py)/api/report/...—report_bp(api/report.py)
Within a blueprint, resource sub-paths are accepted but action-style
endpoints are equally common (/ontology/generate,
/project/<id>/reset). Don't force REST verbs onto operations that
aren't naturally CRUD — match the surrounding file.
The Vite dev server proxies /api/* from :3000 to :5001. Don't
hard-code the backend host in frontend code.
Response Envelope
Every response uses this shape — do not invent a new one:
// Success
{ "success": true, "data": { ... } }
// Failure
{ "success": false, "error": "Human-readable message" }
successis always present and boolean.- Successful responses put the payload under
data. List endpoints may also include sibling fields (count, etc.) — see/project/listfor the precedent. - Error responses use
error: <string>. There is no error-code enum. Messages may be in English or Chinese to match the rest of the module — keep both styles working. - HTTP status follows the outcome:
200on success,400for client validation,404for missing entities,500for unhandled exceptions.
Long-Running Operations: The Task Polling Contract
This is the defining backend pattern. Anything that takes more than a few seconds (ontology generation, graph build, profile generation, simulation, report generation) must use it.
Submit endpoint
- Validates input synchronously.
- Creates a
TaskviaTaskManager().create_task(task_type, metadata). - Spawns a background
threading.Threadthat runs the work and updates the task as it progresses. - Returns immediately:
{ "success": true, "data": { "task_id": "...", "project_id": "..." } }
Background worker
- Calls
TaskManager().update_task(task_id, progress=…, message=…, progress_detail=…)at meaningful checkpoints (not every loop iteration). - On success:
complete_task(task_id, result_dict). - On failure:
fail_task(task_id, error_string)— never let the exception escape the thread; tasks must always reach a terminal state.
Status endpoint
- A polling endpoint (typically
/api/<domain>/task/<task_id>or similar) returns the currentTask.to_dict(). - The frontend service layer (
frontend/src/api/*.js) handles exponential backoff + a 5-min timeout; new endpoints don't need custom retry logic on the client.
Task lifecycle
PENDING → PROCESSING → COMPLETED | FAILED. Other status fields
(progress 0–100, progress_detail dict) are advisory — the frontend
decides how to render them. Don't add new statuses without a
frontend-side change.
Where Logic Belongs
api/(handlers): validate input, look upProject/Task, dispatch to a service, format the envelope. No graph access, no LLM calls, nosubprocess.services/: all business logic, including spawning the background thread for long-running work.models/: state shape only.
If a handler is doing more than a few lines of orchestration, the work belongs in a service.
Authentication
There is no user-level authentication today. Endpoints assume a trusted operator on the same network (dev, Docker, internal deployment). Do not add ad-hoc auth checks scattered through handlers — if/when auth is needed, it goes through Flask middleware and is documented in a new steering file. Until then, treat all endpoints as authenticated by deployment.
Versioning
No version prefix in URLs. The frontend ships with the backend in a single repo, so backwards compatibility for the API is not a concern. If that ever changes (public API, multiple frontend versions), version the affected blueprint, not the whole API.
Pagination
/project/listaccepts?limit=<n>(default 50). Match this pattern for new list endpoints.- Graph queries use
utils/zep_paging.pyfor cursor-style paging (legacy name; still the canonical helper).
What Not to Do
- Don't return raw exceptions or stack traces in
error. - Don't bypass
TaskManagerfor long-running work (e.g. with a custom status field onProject). - Don't add new response envelope shapes — extend
data. - Don't introduce streaming (SSE, websockets) without a steering-level discussion; the polling model is intentional.
Focus on patterns and decisions, not endpoint catalogs.