1. Technical Deep Dive
  2. Backend API Reference

​
Backend API Reference

All endpoints are prefixed with /api and served by the FastAPI application defined in backend/main.py. The API is stateless—no session cookies or JWTs are required by default (suitable for local development). Every request/response body is JSON.

The FastAPI interactive docs are available live at http://localhost:8001/api/docs.


​
Authentication

There is currently no authentication layer. If you deploy Mako Code to a public subnet, protect it via reverse-proxy (basic auth) or add an AuthRouter using FastAPI dependencies.


​
Table of Contents

  1. POST /api/execute – Execute arbitrary Python or SQL.
  2. POST /api/lint – Lint Python source via Ruff.
  3. POST /api/dataset – Upload dataset (called by DataImportModal).
  4. GET /api/dataset/{path} – Fetch dataset schema & preview.
  5. DELETE /api/dataset – Delete dataset.
  6. GET /api/dataset/context/{name} – Retrieve dataset context JSON.
  7. POST /api/dataset/context – Update context file.
  8. GET /api/version/{tab}/{filename} – Load historical code snapshot.
  9. POST /api/rename-version-folder – Rename a tab’s version folder.
  10. POST /api/functions – Save user function.
  11. DELETE /api/functions/{name} – Delete saved function.

​
1 – POST /api/execute

Execute arbitrary Python (default) or SQL. The request model is defined in backend/main.py:

class CodeRequest(BaseModel):
    code: str
    language: Literal["python", "sql"] = "python"

​
Request

{
  "code": "print(2 + 2)",
  "language": "python"
}

​
Response

{
  "stdout": "4\n",
  "stderr": "",
  "figures": [],
  "success": true
}

If language = sql, the backend routes the snippet through functions.utils.execute_sql. Only basic SELECT … FROM queries are supported. The target table must match a dataset name in DATASETS_DIR.


​
2 – POST /api/lint

Lint Python source code using Ruff. Implementation excerpt:

@app.post("/api/lint")
async def lint_code(request: LintRequest):
    try:
        result = subprocess.run([
            "ruff", "--stdin-filename", request.filename, "-"],
            input=request.code.encode(),
            capture_output=True,
        )
        return {"output": result.stdout.decode()}
    except FileNotFoundError as se:
        return {"error": str(se)}

Expect standard Ruff output. Integrations inside +page.svelte parse this into inline annotations.


​
3 – Dataset Endpoints

​
3.1 POST /api/dataset

Multipart upload expecting form-data field file.

POST /api/dataset
Content-Type: multipart/form-data; boundary=---123

---123
Content-Disposition: form-data; name="file"; filename="sales.parquet"
Content-Type: application/octet-stream

<binary>
---123--

The backend:

@app.post("/api/dataset")
async def upload_dataset(file: UploadFile):
    target = ensure_dataset_dir() / file.filename
    target.write_bytes(await file.read())
    df = pl.read_parquet(target, n_rows=5)
    return {"path": str(target), "preview": df.to_dict(as_series=False)}

​
3.2 GET /api/dataset/{path}

Responds with schema, row count, and sample preview. Consumed by DataframeView.svelte.

{
  "schema": [
    ["customer_id", "Int64"],
    ["total_price", "Float64"]
  ],
  "num_rows": 25786,
  "preview": [...]
}

​
3.3 DELETE /api/dataset

Body:

{"path": "data/datasets/old.parquet"}

Returns { "deleted": true } on success.


​
4 – Versioning Endpoints

GET /api/version/{tab}/{filename} streams the raw Python file for historical view. VersionHistory.svelte parses timestamps from filenames and displays them in an accordion list.

@app.get("/api/version/{tab}/{version_filename}")
async def get_version(tab_name: str, version_filename: str):
    version_path = Path("data/versions") / tab_name / version_filename
    return Response(version_path.read_text(), media_type="text/x-python")

​
5 – Function Management

User helper functions are stored one-per-file under data/functions/. Each file has YAML front-matter describing the function (docstring, author, created_at). The user can enable/disable them via FunctionsModal.svelte.

​
5.1 POST /api/functions

{
  "name": "my_udf",
  "code": "def my_udf(df): return df.filter(pl.col('a') > 0)",
  "description": "Filter out negative a's"
}

If the name already exists the backend overwrites the file.

​
5.2 DELETE /api/functions/{name}

Removes the file and returns { "deleted": true }.


​
Status Codes & Error Model

CodeMeaningNotes
200OKSuccessful operation
400Bad RequestCode safety validation failed, or payload invalid
404Not FoundDataset or function missing
500Internal Server ErrorUncaught exception inside execution sandbox

Error responses are always JSON with at least an error field.


​
Pagination & Limits

Dataset preview endpoints accept query parameters offset and limit (default 0 and 100). This is enforced in backend/main.py#get_dataset_data:

rows = df.slice(request.offset, request.limit).to_dict(as_series=False)

Clients must request subsequent pages explicitly.


​
WebSocket Roadmap

Future versions may expose a /ws/execute channel for streaming stdout in real time. Contributions welcome; see Contributing Guide.