MicroFish/.kiro/steering/api-standards.md

5.0 KiB
Raw Blame History

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/Task data (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" }
  • success is always present and boolean.
  • Successful responses put the payload under data. List endpoints may also include sibling fields (count, etc.) — see /project/list for 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: 200 on success, 400 for client validation, 404 for missing entities, 500 for 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 Task via TaskManager().create_task(task_type, metadata).
  • Spawns a background threading.Thread that 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 current Task.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 0100, 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 up Project/Task, dispatch to a service, format the envelope. No graph access, no LLM calls, no subprocess.
  • 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/list accepts ?limit=<n> (default 50). Match this pattern for new list endpoints.
  • Graph queries use utils/zep_paging.py for 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 TaskManager for long-running work (e.g. with a custom status field on Project).
  • 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.