# How the Optional Python FastAPI Sidecar Integrates for Raster and Vector Processing in GeoLibre

> Discover how the optional Python FastAPI sidecar in GeoLibre integrates for raster and vector processing, offloading geoprocessing tasks to Python libraries and browser engines.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-04

---

**GeoLibre's optional Python FastAPI sidecar provides a local HTTP server that offloads heavy geoprocessing tasks to Python's raster and vector libraries, falling back to browser-based engines when unavailable.**

GeoLibre is designed as a lightweight desktop UI that runs primarily in the browser, but geographic information systems often demand computational power beyond what JavaScript can efficiently provide. The project solves this through an optional **Python FastAPI sidecar** living in `backend/geolibre_server/`, which exposes REST endpoints for raster and vector operations. This architecture lets the desktop application delegate to Python's rich geospatial ecosystem—including **rasterio**, **contourpy**, **GeoPandas**, **Shapely**, and **NumPy**—while maintaining seamless operation when the sidecar is absent.

## Sidecar Lifecycle and Availability Detection

The FastAPI sidecar follows an on-demand startup pattern integrated with GeoLibre's Tauri shell.

### Startup Mechanism

The desktop application launches the sidecar using **uv** for reproducible, frozen environments:

```bash
uv run --frozen --project backend/geolibre_server

```

The server binds to `127.0.0.1` by default. For browser builds, Tauri reverse-proxies requests to `/sidecar`, making the sidecar appear as a same-origin endpoint.

### Runtime Capability Advertisement

Before presenting processing options, the UI queries **status endpoints** that gracefully handle missing dependencies:

- **`GET /vector/status`** — Returns `{"available": true}` if GeoPandas imports successfully
- **`GET /raster/status`** — Returns `{"available": true}` if rasterio and contourpy import successfully

Both endpoints catch `ImportError` exceptions and return friendly JSON failures rather than crashing. This allows the React UI to automatically enable or disable the "Run" button based on actual capability.

*Implementation details:* [`backend/geolibre_server/geolibre_server/app/vector.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/vector.py) (lines 61–74) and [`backend/geolibre_server/geolibre_server/app/raster.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/raster.py) (lines 14–28).

## Vector Processing Flow

The vector pipeline handles GeoJSON transformations through a clean separation between HTTP transport and computational logic.

### Request Structure

The `VectorToolRequest` Pydantic model defines the contract:

```python
class VectorToolRequest(BaseModel):
    tool_id: str
    geojson: Optional[Dict] = None
    parameters: Dict = {}

```

*Source:* [`backend/geolibre_server/geolibre_server/app/vector.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/vector.py), lines 44–48.

### Execution Path

The **`POST /vector/run`** endpoint in [`vector.py`](https://github.com/opengeos/GeoLibre/blob/main/vector.py) (lines 77–107) follows this sequence:

1. Unmarshals the request into `VectorToolRequest`
2. Verifies GeoPandas availability via `vector_ops.geopandas_import_error()`
3. Invokes `geolibre_server.vector_ops.run_vector_tool()`—a **framework-free module** containing the actual algorithms
4. Returns `{"geojson": <result>, "messages": [...]}`

The endpoint uses a **synchronous function signature**, causing FastAPI to run it in a thread pool. This keeps the event loop responsive for status polling while CPU-bound geometry operations execute.

### Cross-Platform Consistency

The same [`vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.py) module serves dual purposes. The Vite plugin [`vite-plugins/copy-vector-ops.ts`](https://github.com/opengeos/GeoLibre/blob/main/vite-plugins/copy-vector-ops.ts) bundles this file into web builds, enabling a **Pyodide fallback** that reproduces identical results in browsers without the sidecar. As documented in [`docs/architecture.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/architecture.md) (around line 77), this ensures behavioral parity between desktop and web deployments.

### Vector Write-Back Security

Vector operations that modify files undergo strict validation in `_validate_write_path` (lines 17–53 of [`vector.py`](https://github.com/opengeos/GeoLibre/blob/main/vector.py)):

- File extension whitelist
- Directory existence and writability
- **Conversion root allowlist** via `GEOLIBRE_CONVERSION_ROOTS`

This prevents path traversal attacks and restricts sidecar file system access to explicitly whitelisted locations.

## Raster Processing Flow

Raster operations follow an **asynchronous job model** suited to long-running GDAL processes.

### Request Structure

The `RasterToolRequest` requires explicit paths:

```python
class RasterToolRequest(BaseModel):
    tool_id: str
    input_path: str
    output_path: str
    parameters: Optional[Dict] = None

```

*Source:* [`backend/geolibre_server/geolibre_server/app/raster.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/raster.py), lines 46–50.

### Execution and Background Jobs

The **`POST /raster/run`** endpoint in [`raster.py`](https://github.com/opengeos/GeoLibre/blob/main/raster.py) (lines 38–82):

1. Validates paths against `_is_within_roots()` from [`conversion.py`](https://github.com/opengeos/GeoLibre/blob/main/conversion.py)
2. Calls `_start_job()` to queue work in the shared conversion job store
3. Launches an embedded Python script (hillshade, slope, reproject, etc.) that executes rasterio/NumPy code
4. Parses progress from special marker strings in subprocess output

This design separates **job management** from **processing logic**, allowing the UI to poll for completion without holding HTTP connections open.

### Status Polling and Results

Raster jobs reuse the conversion job API:

- **`GET /conversion/jobs/{id}`** — Returns current status, progress percentage, and console output
- Upon completion: `{"status": "completed", "output_path": "...", "messages": [...]}`

The raster endpoint thus integrates with GeoLibre's existing conversion infrastructure rather than inventing parallel machinery.

## Security Architecture: The Conversion Allowlist

Both vector and raster pipelines share a unified security boundary through **`GEOLIBRE_CONVERSION_ROOTS`**.

| Operation | Validation Function | Location |
|-----------|---------------------|----------|
| Vector file writes | `_validate_write_path` | [`vector.py`](https://github.com/opengeos/GeoLibre/blob/main/vector.py) lines 17–53 |
| Raster input/output | `_validate_paths` | Imported from [`conversion.py`](https://github.com/opengeos/GeoLibre/blob/main/conversion.py) |

These helpers guarantee that any file path the sidecar touches resides under an explicitly configured directory tree. This mitigates arbitrary file system access even if request payloads are malformed or malicious.

## Desktop UI Integration

The React frontend coordinates sidecar availability through its **Zustand store**:

```tsx
// Check sidecar availability on startup
async function checkSidecar() {
  const [vec, ras] = await Promise.all([
    fetch('http://127.0.0.1:8765/vector/status').then(r => r.json()),
    fetch('http://127.0.0.1:8765/raster/status').then(r => r.json()),
  ]);
  console.log('Vector sidecar', vec.available);
  console.log('Raster sidecar', ras.available);
}

```

When both endpoints return `available: true`, the UI enables a **"Run locally (WASM) → Sidecar"** toggle. This lets users choose between:

- **Client-side engines** (Turf.js or Pyodide) for privacy and offline operation
- **Python sidecar** for large files, native GDAL formats, or algorithms unavailable in JavaScript

If the sidecar is unavailable—missing GeoPandas, for instance—the UI automatically falls back to browser-based processing without user intervention.

## Practical Usage Examples

### Calling the Vector Endpoint

```python
import requests

payload = {
    "tool_id": "buffer",
    "geojson": {
        "type": "FeatureCollection",
        "features": [...]
    },
    "parameters": {"distance": 1000, "units": "meters"}
}

resp = requests.post(
    'http://127.0.0.1:8765/vector/run',
    json=payload
)
result = resp.json()
print(result['geojson'])

```

### Running a Raster Hillshade Job

```python
import requests
import time

# Start job

run_payload = {
    "tool_id": "hillshade",
    "input_path": "/data/dem.tif",
    "output_path": "/output/hillshade.tif",
    "parameters": {"azimuth": 315, "altitude": 45}
}
resp = requests.post(
    'http://127.0.0.1:8765/raster/run',
    json=run_payload
)
job_id = resp.json()['job_id']

# Poll for completion

while True:
    status = requests.get(
        f'http://127.0.0.1:8765/conversion/jobs/{job_id}'
    ).json()
    if status['status'] == 'completed':
        print('Result file:', status['output_path'])
        break
    time.sleep(0.5)

```

## Key Source Files

| Component | Path | Purpose |
|-----------|------|---------|
| Sidecar entry point | [`backend/geolibre_server/geolibre_server/app/main.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/main.py) | FastAPI application factory, route registration |
| Vector HTTP API | [`backend/geolibre_server/geolibre_server/app/vector.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/vector.py) | `/vector/status`, `/vector/run` endpoints |
| Shared vector algorithms | [`backend/geolibre_server/geolibre_server/vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/vector_ops.py) | Framework-free processing logic, Pyodide-compatible |
| Raster HTTP API | [`backend/geolibre_server/geolibre_server/app/raster.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/raster.py) | `/raster/status`, `/raster/run` endpoints |
| Job management & security | [`backend/geolibre_server/geolibre_server/app/conversion.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/conversion.py) | `_is_within_roots`, `_start_job`, polling infrastructure |
| Runtime detection | [`backend/geolibre_server/geolibre_server/app/runtime.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/app/runtime.py) | Environment bootstrap, import error handling |
| Architecture documentation | [`docs/architecture.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/architecture.md) | Pyodide bundling strategy, design rationale |

## Summary

- **On-demand startup**: The Tauri shell launches the FastAPI sidecar via `uv run` when processing demands exceed browser capabilities
- **Graceful degradation**: Status endpoints (`/vector/status`, `/raster/status`) advertise availability; missing dependencies never crash the server
- **Unified security model**: `GEOLIBRE_CONVERSION_ROOTS` restricts all file operations to whitelisted directories
- **Framework-free algorithms**: [`vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.py) runs identically in the sidecar and Pyodide, ensuring consistent results across deployment targets
- **Async job pattern**: Raster operations use background jobs with polling, while vector operations leverage FastAPI's thread pool for synchronous CPU work

## Frequently Asked Questions

### What happens if GeoPandas or rasterio is not installed?

The sidecar starts successfully but returns `{"available": false}` from the respective status endpoint. The UI detects this and disables the sidecar toggle, falling back to Turf.js for vectors or disabling raster tools entirely. No errors propagate to the user.

### Can I use the sidecar with the web/browser build of GeoLibre?

Yes. When building for web, Tauri reverse-proxies the sidecar at `/sidecar` so the same origin policy is satisfied. Alternatively, you can deploy the FastAPI server independently and configure the UI to point at it—though this requires handling CORS and authentication yourself.

### Why does raster processing use background jobs while vector processing does not?

Raster operations typically invoke GDAL through rasterio, which may run for minutes on large datasets. The job model prevents HTTP timeouts and allows progress tracking. Vector operations are usually faster (geometry buffering, intersection, etc.), so FastAPI's default thread pool provides sufficient concurrency without the complexity of job state management.

### Is the sidecar required for GeoLibre to function?

No. The application is designed to work entirely in the browser using JavaScript libraries (Turf.js) and WebAssembly (Pyodide). The Python FastAPI sidecar is strictly an **optional accelerator** for heavy workloads, large files, and formats only readable by GDAL.