<@ULVA73B9P> is there a way to integrate the flow ...
# ask-marvin
r
@Marvin is there a way to integrate the flow diagram into a custom ui? i have a flow which gets triggered after a file upload from a html page, i would like to see if i can show the different stages of the run within the ui itself, instead of linking the prefect web url
m
thought for 292.4 seconds
Short answer: there isn’t a prebuilt, embeddable “Prefect flow graph” widget today. But you can absolutely render your own graph and live status in your app by pulling data from the Prefect API. Here’s a practical way to do it in Prefect 3.x: What to call - Flow graph (nodes + edges): use the flow-run graph endpoint to get the dependency graph for a specific flow run - read-flow-run-graph-v1 - read-flow-run-graph-v2 (newer shape; better for mapped tasks) - Task run states (for coloring nodes): filter task runs by flow_run_id - read-task-runs - Flow run state (overall status, start/end times, etc.): - read-flow-run Recommended architecture - Server-side “proxy” (FastAPI/Express/etc.): - Keeps your Prefect API key private and avoids CORS headaches with the Prefect API - Fetches the flow-run graph once (v2) to build nodes/edges for your front-end - Polls task runs by flow_run_id and returns the latest states to your front-end - Front-end: - Render the graph with a library like Cytoscape.js or D3 - Periodically refresh node colors/state badges by calling your proxy for updated task run states Example: simple proxy endpoints Note: Use your PREFECT_API_URL and authentication (Authorization: Bearer <key>) if you’re on Cloud. Self-hosted Server typically doesn’t require auth.
Copy code
# Example FastAPI proxy (minimal)

import os
import httpx
from fastapi import FastAPI, HTTPException

PREFECT_API_URL = os.environ.get("PREFECT_API_URL", "<http://127.0.0.1:4200/api>")  # Server default
PREFECT_API_KEY = os.environ.get("PREFECT_API_KEY")  # Cloud only

def headers():
    h = {"Content-Type": "application/json"}
    if PREFECT_API_KEY:
        h["Authorization"] = f"Bearer {PREFECT_API_KEY}"
    return h

app = FastAPI()

@app.get("/flow-runs/{flow_run_id}/graph")
async def get_flow_run_graph(flow_run_id: str):
    # Prefer the v2 graph
    url = f"{PREFECT_API_URL}/flow_runs/graph/{flow_run_id}/v2"
    async with httpx.AsyncClient() as client:
        resp = await <http://client.post|client.post>(url, headers=headers(), json={})
    if resp.status_code != 200:
        raise HTTPException(resp.status_code, resp.text)
    return resp.json()

@app.get("/flow-runs/{flow_run_id}/task-runs")
async def get_task_runs(flow_run_id: str):
    # Filter task runs by flow_run_id
    url = f"{PREFECT_API_URL}/task_runs/filter"
    payload = {
        "task_runs": {
            "flow_run_id": {"any_": [flow_run_id]}
        }
    }
    async with httpx.AsyncClient() as client:
        resp = await <http://client.post|client.post>(url, headers=headers(), json=payload)
    if resp.status_code != 200:
        raise HTTPException(resp.status_code, resp.text)
    return resp.json()

@app.get("/flow-runs/{flow_run_id}")
async def get_flow_run(flow_run_id: str):
    url = f"{PREFECT_API_URL}/flow_runs/{flow_run_id}"
    async with httpx.AsyncClient() as client:
        resp = await client.get(url, headers=headers())
    if resp.status_code != 200:
        raise HTTPException(resp.status_code, resp.text)
    return resp.json()
Example: front-end polling loop (pseudo-JS) - Render the graph once from /graph - Then poll /task-runs every second or two to update node colors ``` const flowRunId = "<your-flow-run-id>" async function loadGraph() { const graph = await fetch(
/flow-runs/${flowRunId}/graph
).then(r => r.json()) // graph.nodes / graph.edges -> build your Cytoscape graph here } async function refreshStates() { const taskRuns = await fetch(
/flow-runs/${flowRunId}/task-runs
).then(r => r.json()) // taskRuns is a list; map { task_run.id -> state.type } and update node styling } // Initial load await loadGraph() // Periodic refresh
setInterval(refreshStates, 1500) ``` Optional: static diagram generation - If you just want a static dependency diagram (not live), Prefect can generate a Graphviz graph for a flow definition with
prefect.visualization.visualize_task_dependencies
. You could export it to SVG and embed it in your page. - But for “live stages,” use the API approach above and color nodes by their current state. Notes and gotchas - Authentication: If you’re on Prefect Cloud, proxy from your backend so you don’t put an API key in the browser. - CORS: Proxying also avoids cross-origin issues. - Updates: Polling every 1–2 seconds is typically sufficient. If you need lower latency, you can also stream logs or events, but polling is simpler to start. - Mapped tasks: Use the v2 graph endpoint; it represents mapped/grouped tasks more cleanly for visualization. Would you like a working starter repo (FastAPI + Cytoscape) that wires this up end-to-end? Also, are you using Prefect Cloud or self-hosted Server? That’ll determine the exact API base URL and auth setup.