Skip to main content

Frontend Canvas and Backend Runtime Integration

This page is not about "building yet another frontend editor", but rather how the Flowgram.ai canvas aligns with AI4J's Java backend execution layer.

Saying "the frontend just calls a few endpoints" misses the most critical fact: what actually aligns the frontend and backend is not the buttons, but 3 contracts.

  • workflow schema contract
  • task lifecycle contract
  • report / result / trace read-side contract

1. Start with the full chain​

The main chain of the current reference implementation is:

Flowgram.ai canvas
-> runtime plugin
-> WorkflowRuntimeServerClient
-> /flowgram/tasks/*
-> FlowGramTaskController
-> FlowGramRuntimeFacade
-> FlowGramRuntimeService
-> node executors / LLM node runner
-> report / result / trace

This chain tells you one thing: AI4J is not responsible for the frontend canvas itself, but rather the formal execution backend behind the canvas.

2. The frontend does not "directly call five endpoints"​

In ai4j-flowgram-webapp-demo, the frontend runtime is wired in through the runtime plugin, not by hand-writing fetch inside components.

Key files:

  • src/plugins/runtime-plugin/create-runtime-plugin.ts
  • src/plugins/runtime-plugin/runtime-service/index.ts
  • src/plugins/runtime-plugin/client/server-client/index.ts

2.1 Two modes are currently supported​

  • browser
  • server

If you want to integrate with the AI4J Java backend, use server mode.

2.2 Who sends requests in server mode​

The frontend binds WorkflowRuntimeServerClient, which centrally calls:

  • POST /flowgram/tasks/validate
  • POST /flowgram/tasks/run
  • GET /flowgram/tasks/{taskId}/report
  • GET /flowgram/tasks/{taskId}/result
  • POST /flowgram/tasks/{taskId}/cancel

This means the frontend should treat the backend as a task API server, not "some node firing off an ad-hoc request".

3. Before the frontend sends to the backend, the schema is rewritten first​

This layer is the most easily overlooked yet actually most critical part of the whole integration.

Key file:

  • ai4j-flowgram-webapp-demo/src/utils/backend-workflow.ts

3.1 UI-only nodes never reach the backend​

Currently, at least the following are filtered out:

  • Comment
  • Group
  • BlockStart
  • BlockEnd

These objects are meaningful to the editor, but meaningless to the executor.

3.2 Frontend types map to backend execution types​

Currently confirmed mappings include:

  • start -> START
  • end -> END
  • llm -> LLM
  • http -> HTTP
  • code -> CODE
  • condition -> CONDITION
  • loop -> LOOP
  • variable -> VARIABLE
  • tool -> TOOL
  • knowledge -> KNOWLEDGE

This shows:

  • frontend display types are not necessarily the same as backend protocol types
  • there is an explicit adaptation layer between the frontend schema and the backend runtime

3.3 Why this step matters​

Without this adaptation layer, the backend would be forced to understand canvas internals, ultimately causing:

  • UI structure leaking into the execution layer
  • frontend iteration dragging the backend contract along
  • custom nodes becoming hard to maintain

So the most robust approach is to treat "pre-execution normalization" as a formal protocol step, not a demo trick.

4. The backend side is more than just a controller​

At least 3 layers participate in the integration on the backend.

4.1 FlowGramTaskController: HTTP exposure layer​

The controller is mounted by default at:

  • ${ai4j.flowgram.api.base-path:/flowgram}

It only exposes the REST entry points; it does not execute business logic.

4.2 FlowGramRuntimeFacade: platform governance layer​

The facade does far more than the controller:

  • converts the request into runtime input
  • resolves the caller
  • performs access checks
  • creates task ownership
  • writes state to FlowGramTaskStore
  • decides whether to include node details / trace based on configuration

If you later need to integrate permissions, a task center, or audit, this layer is the real backend boundary.

4.3 FlowGramRuntimeService: the actual execution layer​

The runtime is responsible for:

  • schema validation
  • task record creation
  • node graph execution
  • report / result generation

This shows the frontend-backend integration is not "frontend JSON -> controller -> done"; there are explicit platform and execution layers in between.

5. How the frontend runtime actually drives a task​

The current main flow of WorkflowRuntimeService is very clear.

5.1 Local form validation first, before submission​

The frontend first walks all node forms to ensure basic form constraints pass.

5.2 Then call /tasks/validate​

This step catches errors from the backend's perspective, such as:

  • invalid graph structure
  • unregistered node types
  • required inputs not bound
  • invalid reference paths

5.3 Once it passes, call /tasks/run​

The backend does not return the final result; it returns a taskId. This means the execution model is an async task.

5.4 The frontend polls report at a fixed interval​

Current frontend constant:

  • SYNC_TASK_REPORT_INTERVAL = 500

That is, by default it polls the task report once every 500ms.

5.5 After it finishes, fetch the result​

The final output is not inferred directly from the report, but formally fetched via /tasks/{taskId}/result.

6. Do not conflate the validate, report, and result responses​

This is the most common mistake when wiring up the frontend.

6.1 validate​

Suitable for:

  • pre-submission blocking
  • form error messaging
  • schema validity confirmation

Not suitable for:

  • runtime UI display

6.2 report​

Suitable for:

  • in-progress status panel
  • node highlighting
  • error localization
  • progress timeline

If reportNodeDetails = true, it also includes node-level inputs / outputs.

6.3 result​

Suitable for:

  • final output display
  • task completion page
  • final success / failure settlement

It leans toward "final result view", while report leans toward "execution process view".

7. trace is a projection for the frontend to consume directly, not raw instrumentation​

When ai4j.flowgram.trace-enabled = true:

  • /report
  • /result

both include a trace field.

This data originates from:

  • FlowGramRuntimeEvent
  • FlowGramRuntimeTraceCollector
  • FlowGramTraceView

7.1 What problem it solves​

The frontend needs:

  • top-level task status
  • node execution timeline
  • which node failed
  • per-node duration and metrics

This kind of UI should not consume raw backend instrumentation objects directly; it should consume the FlowGramTraceView projected for the frontend.

7.2 Why not just let the frontend read OTel directly​

Because the two layers have different goals:

  • OTel targets backend observability platforms
  • FlowGramTraceView targets the frontend canvas runtime

Keeping them separate makes the frontend-backend responsibility boundary clearer.

8. The role of the Protocol Adapter here​

FlowGramProtocolAdapter is one of the protocol convergence points between frontend and backend.

It wires together:

  • HTTP request DTOs
  • runtime input/output models
  • response DTOs consumed by the frontend

Why it matters​

Because it guarantees a few key facts:

  • the request schema can be an object or a JSON string
  • the response is copied into a safer map structure
  • the external field shape of report / result is fixed by the adapter, not leaked from runtime internal objects

This makes protocol evolution more controllable than directly exposing internal models.

9. The 4 most common integration issues​

9.1 The node renders on the frontend, but the backend does not recognize it​

The usual causes are:

  • type mapping missing
  • backend has not registered the corresponding executor
  • the node was mistakenly filtered out as a UI-only type

9.2 The form looks filled in, but the backend still reports missing required fields​

The usual causes are:

  • frontend form field names don't match the field names the backend reads
  • the binding is written in data but never enters inputsValues
  • fields lost during schema normalization

9.3 The task runs, but the report shows no node detail​

Usually check:

  • whether reportNodeDetails is enabled
  • whether the frontend is reading workflow.nodes
  • whether the node outputs are written out properly by the executor

9.4 Cannot get task details after a restart​

This is an architectural boundary, not necessarily a bug.

Currently the source of truth for running state lives mainly in the in-process TaskRecord of FlowGramRuntimeService. FlowGramTaskStore persists metadata and snapshots, but it is not a fully recoverable execution engine.

10. Where the permission and multi-tenancy integration points are​

Default is a light-security posture

The current default is a light-security posture:

  • auth.enabled = false
  • caller is anonymous by default
  • access checker allows all by default

If you want to integrate with an enterprise platform, the key integration points are:

  • FlowGramCallerResolver
  • FlowGramAccessChecker
  • FlowGramTaskOwnershipStrategy

In other words, permission control lives not in the frontend runtime plugin, but in the backend facade layer.

Additionally, the frontend canvas and the backend task API are usually deployed on different origins, so cross-origin browser calls to /flowgram/** require CORS to be allowed by the backend: add the editor's origin to ai4j.flowgram.cors.allowed-origins (empty by default, bound to CorsProperties). For the full field list, see Spring Boot Configuration Reference §5 (ai4j.flowgram.* / CORS).

11. The most important integration principle​

The most robust integration approach is not to let the frontend know more backend details, but to hold these 3 boundaries:

  • the canvas layer owns editing and display
  • the adaptation layer owns compressing edit state into execution state
  • the backend owns the task lifecycle and node execution

Once these 3 boundaries are stable, frontend and backend can each iterate without dragging each other down.