How to Build a Cross-Platform Desktop App with Electron and FastAPI: Architecture and Implementation Guide

To build a cross-platform desktop app with Electron and FastAPI, run the FastAPI server as a subprocess managed by Electron's main process, communicating over HTTP while exposing a secure, type-checked API to the renderer via a preload script.

Building a cross-platform desktop app with Electron and FastAPI requires careful separation between the UI layer and the Python backend to prevent crashes in heavy inference tasks from affecting the frontend. The Modly repository demonstrates this pattern by embedding a FastAPI server within an Electron application, using a PythonBridge class to manage the subprocess lifecycle and health monitoring. This architecture enables native desktop capabilities with a React frontend while leveraging Python's ecosystem for AI-driven backend operations.

Architecture of an Electron-FastAPI Desktop App

Electron Main Process and Window Management

The Electron main process serves as the orchestration layer, bootstrapping both the native window and the Python backend. In electron/main/index.ts, the application creates a BrowserWindow instance and initializes the PythonBridge before the renderer loads.

The main process handles three critical responsibilities: window lifecycle management, IPC handler registration via setupIpcHandlers(), and auto-update initialization through initAutoUpdater(). By spawning the FastAPI server before the renderer appears, the UI can immediately reach the backend at http://127.0.0.1:8765 without timing issues.

Python Bridge Process Manager

The PythonBridge class in electron/main/python-bridge.ts wraps the FastAPI subprocess, providing health monitoring and log forwarding. When pythonBridge.start() executes, it launches the server via uvicorn and polls the /health endpoint until the backend signals readiness.

This bridge implements platform-specific process termination logic at lines 68-73, using process groups on Unix systems and taskkill on Windows to guarantee clean shutdown. The bridge also captures stdout and stderr from the Python process, emitting log messages to the renderer through IPC to enable real-time debugging in the UI.

Secure Preload Script API

Security isolation requires the renderer to access Node.js capabilities only through a carefully controlled preload layer. The electron/preload/electron-api.ts file uses contextBridge.exposeInMainWorld to inject a window.electron object containing type-safe methods.

The exposed API includes filesystem operations like window.electron.fs.readFileBase64(), window controls such as window.electron.window.minimize(), and backend configuration via window.electron.api.baseUrl. This pattern prevents untrusted renderer code from executing arbitrary shell commands while maintaining full functionality.

React Renderer Integration

The React components interact with the backend exclusively through the preloaded API. For example, src/shared/components/layout/TopBar.tsx invokes window.electron.window.minimize() and window.electron.window.close() to implement custom title bar controls.

Data fetching occurs through standard HTTP requests to the FastAPI server, with the preload script providing the base URL. Custom hooks like those in src/shared/hooks/useApi.ts handle binary encoding and JSON serialization, calling endpoints such as /workflow-runs/from-image to trigger AI generation pipelines.

FastAPI Backend Services

The Python layer resides in api/main.py, configuring CORS and mounting routers for generation tasks. The api/routers/generation.py file defines REST endpoints that accept base64-encoded images and coordinate with the extension registry to execute model inference.

Unlike typical web applications, this FastAPI instance listens on localhost only, accepting connections exclusively from the Electron main process and renderer. The backend maintains stateless REST conventions while supporting long-running workflow executions that stream progress updates back to the UI.

Cross-Platform Desktop Implementation

Process Isolation and Stability

Running FastAPI as a separate subprocess isolates memory-intensive Python operations from the Chromium renderer process. If a model inference exhausts available RAM or triggers a segmentation fault, the PythonBridge detects the exit code and can restart the backend without crashing the Electron window.

This architecture also enables CPU throttling and resource monitoring; the main process tracks Python subprocess health independently of UI responsiveness, ensuring the native window controls remain fluid even during heavy computation.

Platform-Specific Process Management

The PythonBridge handles cross-platform discrepancies in process termination. On macOS and Linux, it utilizes process groups to ensure child processes of the Python interpreter terminate alongside the main uvicorn process. On Windows, it explicitly invokes taskkill with the /F flag to prevent zombie processes when the user closes the application.

Platform detection also influences the build pipeline. Packaging scripts embed the Python virtual environment directly into the Electron bundle, creating platform-specific binaries that include the correct Python interpreter for Windows, Linux, or Apple Silicon macOS without requiring external installations.

Auto-Updates and Extension Loading

The initAutoUpdater() function integrated in electron/main/index.ts handles silent updates for both the Electron frontend and the Python backend. The extension system managed by api/services/generator_registry.py supports hot-reloading of third-party generators from GitHub repositories containing manifest.json files, allowing users to install AI models without restarting the application.

Implementation Examples

Spawning the FastAPI Server from Electron

// electron/main/index.ts (excerpt)
pythonBridge = new PythonBridge()
pythonBridge.setWindowGetter(() => mainWindow)
setupIpcHandlers(pythonBridge, () => mainWindow)   // registers IPC for window ops
initAutoUpdater(() => mainWindow)                 // auto‑update support
pythonBridge.start()                              // launches FastAPI via uvicorn

Exposing Type-Safe APIs via Preload

// electron/preload/electron-api.ts (excerpt)
contextBridge.exposeInMainWorld('electron', {
  fs: {
    readFileBase64: async (path: string) => {
      const res = await ipcRenderer.invoke('fs:readFileBase64', path)
      return res as string
    }
  },
  window: {
    minimize: () => ipcRenderer.invoke('window:minimize')
  },
  api: {
    baseUrl: 'http://127.0.0.1:8765'   // injected by PythonBridge
  }
})

Calling Backend Endpoints from React

// src/shared/hooks/useApi.ts (excerpt)
export async function generateMesh(imagePath: string) {
  const base64 = await window.electron.fs.readFileBase64(imagePath)
  const resp = await fetch(`${window.electron.api.baseUrl}/workflow-runs/from-image`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ image: base64 })
  })
  return resp.json()
}

Defining REST Routes in FastAPI


# api/routers/generation.py (excerpt)

@router.post("/workflow-runs/from-image")
async def generate_from_image(payload: ImagePayload):
    # … invoke model extension, stream progress, return final mesh URL …

    return {"status": "completed", "mesh_url": mesh_path}

Extension System and Hot Reloading

The generator_registry.py service scans the user data directory for extension folders containing manifest.json files, dynamically importing Python modules without restarting the FastAPI server. This enables third-party developers to distribute AI models as GitHub repositories that users can install through the UI.

The registry maintains an in-memory cache of available generators, refreshing the list when file system watchers detect new installations. This hot-reloading capability ensures the desktop app remains responsive while updating its backend capabilities, a critical feature for AI tools requiring frequent model updates.

Developer Workflow and CLI Integration

Running npm run dev starts both the Electron main process and the FastAPI server in watch mode, enabling hot module replacement for the React frontend and auto-restart for the Python backend. For headless automation or CI pipelines, the tools/modly-cli/agent.py script provides direct access to the FastAPI endpoints without launching the Electron window.

This dual-interface architecture allows developers to test backend logic through the CLI while building UI components against the same REST API, ensuring consistency between automated tests and desktop interactions.

Summary

  • Process isolation prevents Python crashes from affecting the Electron renderer by running FastAPI as a managed subprocess on port 8765.
  • Platform-specific termination logic in electron/main/python-bridge.ts ensures clean shutdown across Windows, Linux, and macOS.
  • Type-safe IPC via electron/preload/electron-api.ts exposes controlled filesystem and window APIs to the React renderer without security vulnerabilities.
  • Hot-reloading extension system allows third-party model installation without restarting the application, managed through api/services/generator_registry.py.
  • Unified development workflow supports both desktop GUI usage and headless CLI testing through the same FastAPI backend.

Frequently Asked Questions

How do you spawn a FastAPI server from an Electron app?

Instantiate the PythonBridge class in electron/main/index.ts and call pythonBridge.start(), which launches the server via uvicorn and polls the /health endpoint until the backend signals readiness. The bridge then injects the base URL into the renderer context, allowing immediate API access once the window appears.

How does the renderer securely communicate with the FastAPI backend?

The preload script at electron/preload/electron-api.ts exposes a window.electron API using contextBridge.exposeInMainWorld, restricting the React renderer to specific IPC channels while blocking direct Node.js access. The renderer fetches data from http://127.0.0.1:8765 or invokes IPC methods for privileged operations like filesystem access.

How do you handle cross-platform process management when bundling Python with Electron?

The PythonBridge implementation uses platform detection to apply Unix process groups or Windows taskkill commands, ensuring the Python subprocess terminates cleanly when the user exits the application. Packaging scripts embed the Python virtual environment into the Electron binary, eliminating external dependencies on end-user machines.

Can you extend the FastAPI backend without restarting the Electron app?

Yes, the extension registry in api/services/generator_registry.py dynamically loads third-party extensions from the user data folder and refreshes the generator list without requiring a FastAPI restart. This enables users to install new AI models from GitHub repositories and immediately use them in the desktop interface.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →