# How the Frontend Communicates with the Backend API in the AI Hedge Fund Project

> Discover how the React frontend communicates with the FastAPI backend using HTTP requests fetch API REST endpoints and SSE for real-time data streaming in the AI hedge fund project.

- Repository: [Virat Singh/ai-hedge-fund](https://github.com/virattt/ai-hedge-fund)
- Tags: how-to-guide
- Published: 2026-03-09

---

**The React frontend communicates with the FastAPI backend exclusively through standard HTTP requests using the browser `fetch` API, leveraging REST endpoints for CRUD operations and Server-Sent Events (SSE) for real-time streaming of hedge fund simulations.**

The `virattt/ai-hedge-fund` repository implements a clean separation between its React-based Vite frontend and Python FastAPI backend. Understanding how the frontend communicates with the backend API is essential for developers extending the platform's real-time financial analysis capabilities.

## Architecture Overview: REST and Server-Sent Events

The communication architecture follows a service-oriented pattern where the frontend encapsulates all network logic in dedicated TypeScript modules. The backend exposes two distinct interaction models:

- **RESTful JSON endpoints** for synchronous CRUD operations on flows and configuration
- **SSE streaming endpoints** for asynchronous, long-running hedge fund executions and backtests

This dual approach allows the UI to maintain responsive interactions while receiving progressive updates from computationally intensive agent simulations.

## Frontend Service Layer

All API interactions originate from the `app/frontend/src/services` directory, which provides typed abstractions over raw `fetch` calls.

### Flow Management via [`flow-service.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/flow-service.ts)

The [`flow-service.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/flow-service.ts) module handles CRUD operations for user-defined agent graphs. Each method constructs URLs against `${API_BASE_URL}/flows/` and returns parsed JSON promises:

```typescript
// app/frontend/src/services/flow-service.ts
export const flowService = {
  async getFlows(): Promise<Flow[]> {
    const r = await fetch(`${API_BASE_URL}/flows/`);
    if (!r.ok) throw new Error('Failed to fetch flows');
    return r.json();  // FastAPI returns List[FlowSummaryResponse]
  },

  async createFlow(data: CreateFlowRequest): Promise<Flow> {
    const r = await fetch(`${API_BASE_URL}/flows/`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    if (!r.ok) throw new Error('Failed to create flow');
    return r.json();  // FastAPI returns FlowResponse
  },
  
  // updateFlow, deleteFlow, duplicateFlow follow identical patterns
};

```

### Hedge Fund Execution via [`api.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/api.ts)

The [`api.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/api.ts) service contains the critical `runHedgeFund` method, which initiates simulations and processes SSE streams. This function posts a `HedgeFundRequest` payload to `/hedge-fund/run`, then consumes the `StreamingResponse` using a `ReadableStream` reader:

```typescript
// app/frontend/src/services/api.ts
export const api = {
  runHedgeFund: (
    params: HedgeFundRequest,
    nodeContext: ReturnType<typeof useNodeContext>,
    flowId: string | null = null
  ): (() => void) => {
    const controller = new AbortController();
    const { signal } = controller;

    fetch(`${API_BASE_URL}/hedge-fund/run`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(params),
      signal,
    })
      .then(r => {
        if (!r.ok) throw new Error(`HTTP ${r.status}`);
        const reader = r.body?.getReader();
        if (!reader) throw new Error('No reader');
        const decoder = new TextDecoder();
        let buffer = '';

        const process = async () => {
          while (true) {
            const { done, value } = await reader.read();
            if (done) break;
            buffer += decoder.decode(value, { stream: true });
            const events = buffer.split('\n\n');
            buffer = events.pop() || '';

            for (const txt of events) {
              const type = txt.match(/^event: (.+)$/m)?.[1];
              const data = txt.match(/^data: (.+)$/m)?.[1];
              if (!type || !data) continue;

              const ev = JSON.parse(data);
              switch (type) {
                case 'start':
                  nodeContext.resetAllNodes(flowId);
                  break;
                case 'progress':
                  const base = ev.agent.replace('_agent', '');
                  const nodeId = params.graph_nodes
                    .find(n => n.id.replace(/\d+_/, '') === base) || base;
                  nodeContext.updateAgentNode(flowId, nodeId, {
                    status: ev.status === 'Done' ? 'COMPLETE' : 'IN_PROGRESS',
                    ticker: ev.ticker,
                    message: ev.status,
                    analysis: ev.analysis,
                    timestamp: ev.timestamp,
                  });
                  break;
                case 'complete':
                  nodeContext.setOutputNodeData(flowId, ev.data);
                  break;
                case 'error':
                  nodeContext.updateAgentNode(flowId, 'portfolio-start', {
                    status: 'ERROR',
                    message: ev.message,
                  });
                  break;
              }
            }
          }
        };
        process();
      })
      .catch(err => console.error('Run error:', err));

    return () => {
      controller.abort();
      if (flowId) flowConnectionManager.setConnection(flowId, { state: 'idle' });
    };
  },
};

```

### Backtesting via [`backtest-api.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/backtest-api.ts)

The [`backtest-api.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/backtest-api.ts) module mirrors the hedge fund execution pattern, posting to `/hedge-fund/backtest` and consuming SSE streams for progressive backtest updates.

## Backend API Implementation

The FastAPI backend in `app/backend/` exposes corresponding endpoints that generate JSON responses or SSE streams.

### FastAPI Routers

The backend organizes routes into dedicated modules:

- **[`app/backend/routes/hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/hedge_fund.py)**: Defines `POST /hedge-fund/run` and `POST /hedge-fund/backtest`. These endpoints receive the request body, construct the agent graph, and return `StreamingResponse` objects that emit SSE events.
- **[`app/backend/routes/flows.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/flows.py)**: Implements standard REST endpoints (`GET /flows/`, `POST /flows/`, `PUT /flows/{id}`, `DELETE /flows/{id}`) used by the frontend flow service.

### Server-Sent Events Streaming

For long-running operations, the backend utilizes FastAPI's `StreamingResponse` to push real-time updates. The implementation in [`hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/hedge_fund.py) follows this pattern:

1. **StartEvent**: Emitted immediately upon request validation
2. **ProgressUpdateEvent**: Pushed as agents complete analysis steps, containing `agent`, `ticker`, `status`, and `analysis` fields
3. **CompleteEvent**: Signifies successful graph execution with final portfolio decisions
4. **ErrorEvent**: Transmits exception details if execution fails

The frontend parses these events using the `event:` and `data:` SSE protocol fields, updating the React node context to render real-time agent progress.

## Real-Time Data Flow Example

When a user initiates a hedge fund simulation, the following communication sequence occurs:

1. The UI invokes `api.runHedgeFund(params, nodeContext, flowId)` from [`app/frontend/src/services/api.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/app/frontend/src/services/api.ts)
2. The service POSTs the `HedgeFundRequest` JSON payload to `http://localhost:8000/hedge-fund/run`
3. FastAPI in [`app/backend/routes/hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/hedge_fund.py) spawns `run_graph_async` and yields a `StartEvent` SSE message
4. As agents process tickers, the backend pushes `ProgressUpdateEvent` objects to an async queue, which the SSE generator reads and formats as `event: progress` messages
5. The frontend's `ReadableStream` reader (`response.body?.getReader()`) consumes chunks, decoding them with `TextDecoder` and splitting on double newlines to extract individual SSE events
6. For each `progress` event, the frontend maps the backend agent name to the corresponding React Flow node ID and calls `nodeContext.updateAgentNode()` to update the visual status
7. Upon receiving the `complete` event, the frontend invokes `nodeContext.setOutputNodeData()` to render the final portfolio decisions

## Error Handling and Connection Management

Both frontend and backend implement robust connection lifecycle management:

- **AbortController**: Every streaming request creates an `AbortController`. If the user navigates away or clicks cancel, the frontend calls `controller.abort()`, which closes the HTTP connection.
- **Client Disconnect Detection**: The backend monitors `await request.receive()` for `http.disconnect` messages. When detected, it cancels the background `asyncio` task to free resources.
- **Error Propagation**: Backend exceptions generate `event: error` SSE messages containing the error details, which the frontend catches in the switch statement to update node statuses to `ERROR` state.

## Summary

- The **React frontend** communicates with the **FastAPI backend** through standard HTTP requests using the browser `fetch` API.
- **CRUD operations** on flows use synchronous JSON endpoints defined in [`app/backend/routes/flows.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/flows.py) and consumed by [`app/frontend/src/services/flow-service.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/app/frontend/src/services/flow-service.ts).
- **Real-time simulations** leverage **Server-Sent Events (SSE)** via `StreamingResponse` in [`app/backend/routes/hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/app/backend/routes/hedge_fund.py), consumed by [`app/frontend/src/services/api.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/app/frontend/src/services/api.ts) using `ReadableStream` readers.
- The frontend maps SSE events (`start`, `progress`, `complete`, `error`) to React context updates via `nodeContext` methods, enabling live visualization of agent execution.
- Both sides implement **graceful abort** mechanisms using `AbortController` and FastAPI disconnect detection to manage long-running connection lifecycles.

## Frequently Asked Questions

### What protocol does the AI Hedge Fund frontend use to receive real-time updates from the backend?

The frontend uses **Server-Sent Events (SSE)**, a standard HTTP-based protocol where the backend streams text events using the `text/event-stream` content type. The frontend consumes these through the `fetch` API's `ReadableStream` interface, parsing `event:` and `data:` fields to update the React UI progressively.

### How does the frontend handle cancellation of a running hedge fund simulation?

The frontend creates an `AbortController` for each streaming request in [`app/frontend/src/services/api.ts`](https://github.com/virattt/ai-hedge-fund/blob/main/app/frontend/src/services/api.ts). When a user cancels the operation, the code calls `controller.abort()`, which closes the HTTP connection. The FastAPI backend detects this disconnect via `http.disconnect` messages and terminates the background `asyncio` task to prevent resource leaks.

### Where are the API endpoints defined in the backend?

The FastAPI routes are modularized under `app/backend/routes/`. The [`flows.py`](https://github.com/virattt/ai-hedge-fund/blob/main/flows.py) router handles CRUD operations for agent graphs at `/flows/`, while [`hedge_fund.py`](https://github.com/virattt/ai-hedge-fund/blob/main/hedge_fund.py) defines the streaming endpoints `/hedge-fund/run` and `/hedge-fund/backtest` that return `StreamingResponse` objects for SSE communication.

### Can the backend API URL be configured for different environments?

Yes. The frontend defaults to `http://localhost:8000` but supports environment-specific configuration through the `VITE_API_URL` environment variable. This value is exposed via Vite's `import.meta.env` mechanism and used throughout the service layer in `app/frontend/src/services/` to construct full endpoint URLs.