FastAPI + React + Tauri Integration
Overview
This guide describes the recommended way to use the Python package in a desktop application built from:
- React (or Svelte) frontend
- FastAPI backend
- Tauri packaging
The key idea is to treat the Python package as a reusable computation library, and to keep UI-specific modules optional.
Recommended runtime flow:
Tauri shell
-> React frontend
-> HTTP calls to FastAPI on localhost
-> FastAPI calls module.services / module.workspace / module.storage
Do not build the Tauri app around module/ui_streamlit.py or module/ui_kivy.py.
Those files are alternative Python frontends, not the ideal integration surface for Tauri.
Recommended package split
Use the package in layers:
module.models- dataclasses and enums shared by the backend
module.services- chart computation and domain logic
module.workspace- workspace loading and saving
module.storage- optional DuckDB and Parquet support
module.cli- existing JSON command contract, useful as a reference
module.ui_*- optional developer-facing UIs, not required by FastAPI or Tauri
For the target architecture, prefer:
- core package install for shared astrology logic
apiextra for FastAPI- optional
visualsextra only if the backend needs Plotly/Matplotlib output
Installation options
Lean backend for Tauri
Install only the backend runtime:
pip install .
pip install .[api]
This is the preferred setup when:
- React renders the UI
- FastAPI exposes JSON endpoints
- the backend does not need Streamlit or Kivy
Backend with chart rendering support
If the Python backend must generate Plotly figures or export images:
pip install .
pip install .[api]
pip install .[visuals]
Optional developer UIs
Install only when those frontends are actually used:
pip install .[streamlit]
pip install .[kivy]
Why this split matters
The repository contains multiple frontend styles:
- Streamlit
- Kivy
- CLI/TUI
- sidecar-style JSON commands
For a Tauri desktop app, shipping unnecessary frontend dependencies makes the Python environment larger and harder to maintain.
In particular:
- Streamlit should not be required by the FastAPI sidecar
- Kivy should not be required by the FastAPI sidecar
- visualization libraries should be optional unless the backend renders charts itself
Existing integration surface
The current repo already has a useful contract in module.cli.
See:
cli_reference.mdmodule/cli.py
That CLI already exposes sidecar-style operations such as:
compute_chartcompute_transit_seriesget_workspace_settingslist_chartsget_chart
You can either:
- Keep using the CLI as a subprocess contract from Tauri, or
- Move that same contract behind FastAPI routes
For new work, FastAPI is usually the cleaner option because it gives you:
- typed request and response models
- easier debugging
- better error handling
- simpler integration from React
- cleaner long-term API evolution
Recommended FastAPI structure
Create a dedicated API layer instead of putting HTTP concerns into module.services.
Suggested layout:
module/
api/
__init__.py
app.py
schemas.py
routes/
charts.py
workspace.py
transits.py
Suggested responsibility split:
module.services- pure domain logic
module.workspace- file and workspace orchestration
module.storage- persistence concerns
module.api.schemas- Pydantic request and response models
module.api.routes.*- HTTP request parsing and response shaping
Example FastAPI adapter
Minimal example:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from module.workspace import load_workspace
from module.services import (
compute_positions_for_chart,
compute_aspects_for_chart,
find_chart_by_name_or_id,
)
app = FastAPI()
class ComputeChartRequest(BaseModel):
workspace_path: str
chart_id: str
include_physical: bool = False
include_topocentric: bool = False
@app.post("/charts/compute")
def compute_chart(payload: ComputeChartRequest):
ws = load_workspace(payload.workspace_path)
chart = find_chart_by_name_or_id(ws, payload.chart_id)
if not chart:
raise HTTPException(status_code=404, detail="Chart not found")
positions = compute_positions_for_chart(
chart,
ws=ws,
include_physical=payload.include_physical,
include_topocentric=payload.include_topocentric,
)
aspects = compute_aspects_for_chart(chart, ws=ws)
return {
"chart_id": payload.chart_id,
"positions": positions,
"aspects": aspects,
}
This keeps the package reusable while giving the React app a clean HTTP API.
The repository now includes a scaffolded FastAPI adapter in:
module/api/app.pymodule/api/schemas.pymodule/api/__main__.py
Run it locally with:
pip install .[api]
python -m module.api --host 127.0.0.1 --port 8765 --reload
Or:
make run-api
Suggested React and Tauri responsibilities
React frontend
The React app should handle:
- routing
- forms
- state management
- chart selection
- API calls to FastAPI
- rendering backend results
Tauri shell
Tauri should handle:
- packaging
- starting and stopping the FastAPI sidecar
- native file dialogs if needed
- desktop-only capabilities
Connecting the pieces together
The clean connection model is:
Tauri
-> starts Python sidecar
-> loads React frontend
-> React waits for GET /health
-> React calls FastAPI routes on localhost
Step 1: start the backend locally
During development, run:
pip install .[api]
python -m module.api --host 127.0.0.1 --port 8765 --reload
Step 2: point React at the local API
Use a frontend API base URL such as:
http://127.0.0.1:8765
Typical frontend calls:
GET /healthGET /workspace/settings?workspace_path=...GET /charts?workspace_path=...GET /charts/{chart_id}?workspace_path=...POST /charts/computePOST /transits/compute-series
Example fetch:
const API_BASE = "http://127.0.0.1:8765";
export async function computeChart(workspacePath: string, chartId: string) {
const response = await fetch(`${API_BASE}/charts/compute`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
workspace_path: workspacePath,
chart_id: chartId,
include_physical: false,
include_topocentric: false,
store_in_db: false,
}),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
Step 3: let Tauri launch the sidecar
In the packaged desktop app, Tauri should launch the bundled backend executable on startup.
The frontend should not assume the backend is instantly ready.
Recommended flow:
- Tauri starts the sidecar.
- React polls
GET /health. - When health is ready, the app unlocks the main UI.
Step 4: keep the backend address configurable
In development:
- use a fixed localhost URL such as
http://127.0.0.1:8765
In production:
- keep the same default if possible
- or inject the backend URL into the frontend on startup
Step 5: pass workspace paths explicitly
The current API scaffold keeps workspace selection explicit.
That means React or Tauri should provide the workspace_path when calling endpoints that need it.
This is a good starting point because it avoids hidden global backend state.
Exact Tauri integration checklist
Use this sequence when you open the Tauri project.
Handoff checklist for the Tauri project
Use one of these two handoff modes.
Option A: source-based handoff
Use this if the Tauri project or CI will build the Python sidecar itself.
Pass these:
module/pyproject.tomlsource/if runtime assets such as ephemeris files are needed- this document:
docs/site/content/fastapi_tauri.md
Do not pass these:
build/.venv-build/- PyInstaller temporary caches
Optional:
kefer-backend.specif you want the Tauri project to reuse the same PyInstaller config
Option B: artifact-based handoff
Use this if the Python backend is built outside the Tauri project and Tauri only bundles the finished sidecar.
Pass these:
dist/kefer-backendordist/kefer-backend.exe- this document:
docs/site/content/fastapi_tauri.md - runtime asset files if the backend expects them beside the executable
Do not pass these:
build/.venv-build/- the full backend source tree, unless the Tauri project also needs to rebuild it
Recommendation
If you want the cleanest Tauri repository, prefer:
- artifact-based handoff for packaging
- source-based handoff only if the Tauri repo is also responsible for backend builds
That keeps the Tauri app smaller and avoids mixing Python build machinery into the frontend project unless you really want one repository to own everything.
Recommended folder strategy
You do not need to flatten everything into one folder.
In fact, that usually becomes harder to maintain.
The cleaner approach is to keep clear boundaries inside the Tauri repository.
Suggested shape:
tauri-app/
frontend/
src-tauri/
backend-python/
module/
pyproject.toml
source/
Or, if the backend is only consumed as a built artifact:
tauri-app/
frontend/
src-tauri/
binaries/
kefer-backend-...
When to keep a separate backend folder
Prefer a separate backend folder when:
- Python is still evolving
- you want backend builds in CI
- you want to keep Python dependencies isolated
- you do not want Tauri frontend folders cluttered with Python files
When artifact-only is better
Prefer artifact-only handoff when:
- the backend is built elsewhere
- the Tauri project should stay mostly frontend plus Rust
- you want the smallest possible mental overhead in the Tauri repo
Recommendation for you
Based on your concern about clutter, I would not merge everything into one giant folder.
I would use one of these:
- a dedicated
backend-python/folder inside the Tauri repo - no backend source in the Tauri repo at all, only the packaged sidecar in
src-tauri/binaries/
Both are cleaner than flattening the whole Python project into the Tauri app root.
Recommended Tauri repo layout
If you plan to pass the backend source and sort it out inside the Tauri project, this is the layout I recommend.
Option 1: keep backend source in the Tauri repo
tauri-app/
frontend/
src/
package.json
vite.config.ts
src-tauri/
Cargo.toml
tauri.conf.json
binaries/
backend-python/
module/
source/
pyproject.toml
Makefile
docs/
Why this is good:
- frontend stays frontend
- Rust and Tauri stay in
src-tauri/ - Python stays isolated in
backend-python/ - CI can build the sidecar from
backend-python/and copy the result intosrc-tauri/binaries/
Option 2: keep only built backend artifacts in the Tauri repo
tauri-app/
frontend/
src/
package.json
src-tauri/
Cargo.toml
tauri.conf.json
binaries/
kefer-backend-x86_64-pc-windows-msvc.exe
kefer-backend-x86_64-unknown-linux-gnu
kefer-backend-aarch64-apple-darwin
kefer-backend-x86_64-apple-darwin
docs/
backend-handoff.md
Why this is good:
- smallest Tauri repo footprint
- no Python build tooling mixed into the desktop app
- Tauri only bundles and launches the sidecar
Which option I recommend
If you are still actively changing the Python backend:
- use Option 1 first
If the backend becomes stable or is built in separate CI:
- move toward Option 2
What I would do in your place
For now, since you plan to pass all of these things to the Tauri project:
- create
backend-python/ - put
module/,source/,pyproject.toml,Makefile, and this doc there - build the sidecar from
backend-python/ - copy the built executable into
src-tauri/binaries/ - let Tauri bundle only the executable, not the whole Python folder
That gives you a clean transition path:
- easy to work on now
- easy to slim down later
Files to ignore in the Tauri project
Even if you pass everything over initially, I recommend not keeping these in the long-term packaged project:
backend-python/build/backend-python/dist/except for the final sidecar you actually bundlebackend-python/.venv-build/- any temporary PyInstaller cache directories
Minimal copy set into backend-python/
Best practical handoff:
module/source/pyproject.tomlMakefiledocs/site/content/fastapi_tauri.md
Optional:
kefer-backend.spec
Step 1: copy or add the Python backend package
Make sure the Tauri app can build this Python project as part of the desktop build.
You need these pieces available to the desktop app build:
module/pyproject.tomlsource/if ephemeris assets are needed at runtime- any workspace/example data you want to ship
If the Tauri app lives in a different repository, either:
- include this repo as a submodule, or
- copy the backend into a dedicated
backend/folder, or - publish the backend package and install it during CI
Step 2: create a backend build environment
For local development:
python -m venv .venv-build
.venv-build/bin/python -m pip install --upgrade pip
.venv-build/bin/python -m pip install -e ".[api]" pyinstaller
On Windows, use:
python -m venv .venv-build
.venv-build\Scripts\python -m pip install --upgrade pip
.venv-build\Scripts\python -m pip install -e ".[api]" pyinstaller
Step 3: verify the backend runs before packaging
Run the backend directly first:
.venv-build/bin/python -m module.api --host 127.0.0.1 --port 8765
Check:
http://127.0.0.1:8765/healthhttp://127.0.0.1:8765/docs
Do this before touching Tauri packaging.
Step 4: build a backend sidecar executable
Start with PyInstaller.
Example command on Linux or macOS:
.venv-build/bin/pyinstaller \
--name kefer-backend \
--onefile \
--collect-all kerykeion \
--hidden-import module.api.app \
--hidden-import module.api.schemas \
-m module.api
On Windows:
.venv-build\Scripts\pyinstaller `
--name kefer-backend `
--onefile `
--collect-all kerykeion `
--hidden-import module.api.app `
--hidden-import module.api.schemas `
-m module.api
Expected output:
dist/kefer-backendon Linux or macOSdist/kefer-backend.exeon Windows
Step 5: add the sidecar binary to the Tauri project
Inside the Tauri project, place the built backend into the sidecar/binaries area used by Tauri.
Typical layout:
src-tauri/
binaries/
kefer-backend-x86_64-pc-windows-msvc.exe
kefer-backend-x86_64-unknown-linux-gnu
kefer-backend-aarch64-apple-darwin
kefer-backend-x86_64-apple-darwin
The exact file names depend on your Tauri target configuration, but the important part is:
- one backend executable per platform target
- copied into the Tauri packaging inputs before the app build runs
Step 6: start the sidecar from Tauri
In the Tauri app:
- start the sidecar on app startup
- pass
--host 127.0.0.1 --port 8765 - capture stdout and stderr for debugging
The Tauri side should:
- launch the bundled
kefer-backend - wait until
GET /healthreturns success - then allow the React app to use the backend
Step 7: point React to the local API
In the React app, use:
http://127.0.0.1:8765
Recommended pattern:
- centralize the base URL in one API client module
- wait for backend readiness before loading workspace-dependent screens
- show a loading or recovery state if the sidecar fails to start
Step 8: wire workspace selection
Because the scaffolded backend is stateless with respect to workspace selection, React or Tauri must provide:
- a workspace file path
Good first implementation:
- use a Tauri file dialog to select
workspace.yaml - store that path in frontend state
- send it on every API request that needs it
Step 9: package the desktop installers
After the sidecar is wired in, build Tauri normally for each platform.
Expected final artifacts:
- Windows
.msior.exe - Linux
.AppImage - macOS
.dmg
Each installer should already contain the backend sidecar.
Step 10: CI artifact strategy
Recommended CI pipeline:
- build backend sidecar per platform
- upload sidecar binaries as CI artifacts
- build Tauri installers using those sidecars
- upload installers as release artifacts
This gives you both:
- backend artifacts for debugging
- final app artifacts for users
FastAPI backend
FastAPI should handle:
- workspace loading
- chart computation
- transit computation
- persistence requests
- normalization of Python exceptions into stable API responses
Suggested endpoint groups
Useful endpoint groups for this project:
GET /healthGET /workspace/settingsGET /chartsGET /charts/{chart_id}POST /charts/computePOST /transits/compute-seriesPOST /storage/export-parquet
These map naturally to the existing logic in module.cli.
Current scaffolded routes:
GET /healthGET /workspace/settingsGET /chartsGET /charts/{chart_id}POST /charts/computePOST /transits/compute-seriesPOST /workspace/syncPOST /storage/export-parquet
Data model guidance
Avoid returning internal dataclasses directly over HTTP.
Instead:
- keep dataclasses in
module.models - map them to Pydantic schemas in
module.api.schemas - use JSON-friendly response shapes
This helps with:
- versioning
- frontend stability
- better OpenAPI docs
- clearer validation errors
What to keep out of the backend core
Do not make the core computation modules depend on:
- Streamlit
- Kivy
- Tauri-specific code
- frontend rendering details
If you need chart visualization, keep it behind an optional boundary.
For example:
- pure position/aspect computation stays in
services - Plotly figure creation stays optional
- UI presentation logic stays in React or separate Python UI modules
CLI vs FastAPI
Both are valid, but they serve different goals.
Use CLI when
- you want the fastest possible integration
- Tauri launches Python subprocesses directly
- request volume is small
Use FastAPI when
- you want a stable backend contract
- React should call normal HTTP endpoints
- you expect the integration to grow
- you want easier debugging and testing
For this project, FastAPI is the better long-term fit.
Migration path from current repo
Recommended path:
- Keep
module.services,module.workspace, andmodule.storageas the core. - Treat
module.clias the source contract for the first API routes. - Add
module/api/with Pydantic schemas and FastAPI routes. - Keep
module/ui_streamlit.pyandmodule/ui_kivy.pyas optional tools for manual testing or demos. - Let Tauri start the FastAPI sidecar and point the React app at it.
Bundling Python into the Tauri app
For shipped desktop builds, do not depend on a user-installed Python.
Instead, bundle the backend privately inside the application package.
There are two main approaches.
Option A: bundle a Python runtime
In this approach, the packaged app contains:
- a Python interpreter
- your installed package
- your dependencies
- a small startup command such as
python -m module.api.app
This works, but it is usually more operationally complex because you need to assemble a platform-specific embedded runtime for:
- Windows
- macOS
- Linux
It is better than requiring a system Python, but it is not the simplest Tauri integration path.
Option B: freeze the Python backend into a standalone executable
In this approach, you build the backend into a self-contained binary with a tool such as:
- PyInstaller
- Nuitka
Tauri then ships that backend binary as a sidecar and starts it on launch.
For this project, this is the recommended approach.
Benefits:
- no dependency on system Python
- simpler installer behavior
- easier Tauri integration
- cleaner artifact story in CI
Recommended approach for this repository
The most practical packaging model here is:
- React frontend inside Tauri
- FastAPI backend as a sidecar executable
- Python package kept normal during development
- frozen backend executable produced per platform during release builds
In development, use venv.
In release packaging, do not ship the development virtual environment directly.
Why not ship the raw virtualenv
A virtual environment is mainly a development convenience.
It is not the best final artifact because:
- it is platform-specific
- it often contains unnecessary development files
- paths inside it can be brittle
- copying it between machines is unreliable
- it usually produces a messier installer than a real sidecar binary
So the typical workflow is:
- develop in a
venv - build a frozen backend executable for each platform
- let Tauri bundle that executable into the final installer
Suggested build artifacts
You will usually end up with two layers of artifacts.
Backend artifacts
These are produced first, one per platform:
backend-windows-x64.zip- contains
kefer-backend.exe
- contains
backend-linux-x64.tar.gz- contains
kefer-backend
- contains
backend-macos-x64.tar.gz- contains
kefer-backend
- contains
backend-macos-aarch64.tar.gz- contains
kefer-backend
- contains
These are CI-friendly artifacts and can be reused by the desktop packaging job.
Desktop installer artifacts
These are the final Tauri deliverables:
- Windows:
.msiand/or.exe - Linux:
.AppImageand optionally.deb - macOS:
.dmg
These final installers should already contain the backend sidecar.
Suggested CI flow
Recommended release pipeline:
- Build frontend assets.
- Build Python backend sidecar for each target platform.
- Upload backend sidecars as CI artifacts.
- Build Tauri app for each target platform.
- Bundle the matching backend sidecar into the Tauri package.
- Upload final installers as release artifacts.
This is a good fit if you want “artifacts will be available as artifacts” in CI, because it gives you:
- intermediate backend artifacts for debugging
- final installer artifacts for users
Suggested repository layout for packaging
Once you add the Tauri app, a practical structure looks like this:
function-wrapper/
module/
frontend/
src-tauri/
tauri.conf.json
Cargo.toml
binaries/
kefer-backend-x86_64-pc-windows-msvc.exe
kefer-backend-x86_64-unknown-linux-gnu
kefer-backend-aarch64-apple-darwin
kefer-backend-x86_64-apple-darwin
The exact sidecar naming depends on the Tauri version and configuration, but the main idea is the same:
- one backend binary per target
- checked into build outputs or copied there during CI
- bundled by Tauri into the final installer
Runtime behavior inside the packaged app
At runtime:
- Tauri launches the bundled backend sidecar.
- The backend starts FastAPI on
127.0.0.1and a fixed or negotiated port. - React calls the local HTTP API.
- On app exit, Tauri stops the sidecar.
Good practice:
- wait for backend readiness before loading API-dependent screens
- expose a small
GET /healthendpoint - log sidecar startup failures clearly
Backend entrypoint recommendation
Create a tiny dedicated backend entrypoint for packaging.
For example:
module/
api/
app.py
__main__.py
That entrypoint should do one job:
- start the FastAPI app with the correct host and port
Keep packaging logic outside the domain modules.
This repository now provides that entrypoint through:
python -m module.apikefer-api
Tooling recommendation
For this repository, start with:
- FastAPI + Uvicorn for the backend
- PyInstaller for the first packaged sidecar
- Tauri sidecar bundling for the desktop app
PyInstaller is not perfect, but it is usually the fastest way to get to real artifacts.
If startup speed or binary size later becomes a problem, evaluate Nuitka after the integration is already working.
Practical release plan
The simplest path to production-like artifacts is:
- Add
module/api/app.py - Add a minimal backend launcher
- Build that backend with PyInstaller for each target platform
- Add the Tauri app and configure it to start the sidecar
- Produce installer artifacts per platform
That gets you to:
- reusable Python package in development
- private bundled backend in release builds
- downloadable CI artifacts for both sidecars and installers
Practical recommendation
If you are building the new application now, the most maintainable setup is:
- Python package for business logic
- FastAPI as the backend adapter
- React as the only application UI
- Tauri as the desktop shell
That gives you one real UI, one real backend contract, and keeps the old Python UI modules optional instead of foundational.