An open API service indexing awesome lists of open source software.

https://github.com/niradler/streamlit-fastapi-proxy


https://github.com/niradler/streamlit-fastapi-proxy

Last synced: about 1 month ago
JSON representation

Awesome Lists containing this project

README

          

# Streamlit FastAPI Proxy

A FastAPI-based proxy server for managing and serving multiple Streamlit applications with full WebSocket support. Can be used as a standalone proxy server or integrated into existing FastAPI applications.

## Features

- 🚀 **Multiple Streamlit Apps**: Run and manage multiple Streamlit applications simultaneously
- 🔌 **WebSocket Support**: Full WebSocket proxying for real-time Streamlit features
- 📡 **HTTP Proxy**: Complete HTTP request/response proxying
- 🎛️ **App Management**: RESTful API for starting, stopping, and managing apps
- 📊 **App Registry**: Persistent storage of registered applications
- 🔄 **Auto Port Management**: Automatic port allocation and management
- 📝 **Access Tracking**: Track last access times for apps
- 🧩 **Easy Integration**: Simple integration into existing FastAPI applications
- 🏁 **Default Apps**: Auto-start apps marked with `run_by_default=True`

![Demo](st-proxy.gif)

## Installation

### From PyPI (Recommended)

```bash
pip install streamlit-fastapi-proxy

# Run the standalone server
streamlit-proxy
```

### From Source

```bash
# Clone the repository
git clone https://github.com/niradler/streamlit-fastapi-proxy.git
cd streamlit-fastapi-proxy

# Install dependencies with uv
uv sync

# Or install manually
pip install -e .

# Run the standalone server
streamlit-proxy
# Or run directly without installing
python -m streamlit_proxy.cli
```

### Using Docker

```bash
# Clone the repository
git clone https://github.com/niradler/streamlit-fastapi-proxy.git
cd streamlit-fastapi-proxy

# Build and run with Docker Compose
docker-compose up -d

# Or build and run manually
docker build -t streamlit-fastapi-proxy .
docker run -d -p 8000:8000 \
-v $(pwd)/app_registry.json:/app/app_registry.json \
-v $(pwd)/apps:/app/apps \
streamlit-fastapi-proxy
```

## Quick Start

### Option 1: Standalone Server

1. **Install the package:**
```bash
pip install streamlit-fastapi-proxy
```

2. **Start the proxy server:**
```bash
streamlit-proxy
# Or run directly from source
python -m streamlit_proxy.cli
```

3. **Register a Streamlit app:**
```bash
curl -X POST "http://localhost:8000/_apps/register" \
-H "Content-Type: application/json" \
-d '{
"name": "Test App",
"slug": "test-app",
"path": "/path/to/your/app.py",
"run_by_default": false
}'
```

4. **Start the app:**
```bash
curl -X POST "http://localhost:8000/_apps/test-app/start"
```

5. **Access your app:**
Open `http://localhost:8000/apps/test-app/` in your browser

### Option 2: Integration with Existing FastAPI App

```python
from fastapi import FastAPI
from streamlit_proxy import integrate_streamlit_proxy

# Create your FastAPI app
app = FastAPI(title="My Dashboard")

# Add your existing routes
@app.get("/")
def read_root():
return {"message": "Hello World"}

# Integrate Streamlit proxy
integrate_streamlit_proxy(app)

# Run with: uvicorn main:app --reload
```

This will add:
- Management endpoints at `/_apps/*`
- Proxy endpoints at `/apps/*`
- CORS middleware (if not already present)

### Option 3: Manual Integration

For more control over the integration:

```python
from fastapi import FastAPI
from streamlit_proxy.main import (
setup_streamlit_proxy_routes,
add_streamlit_proxy_middleware,
)

app = FastAPI()

# Add routes with custom prefix
setup_streamlit_proxy_routes(app)

# Optionally add CORS middleware
add_streamlit_proxy_middleware(app)
```

## API Endpoints

### App Management

- **GET** `/_apps/` - List all registered apps with status
- **POST** `/_apps/register` - Register a new Streamlit app
- **POST** `/_apps/{slug}/start` - Start a specific app
- **POST** `/_apps/{slug}/stop` - Stop a specific app
- **GET** `/_apps/{slug}/status` - Get detailed status of an app
- **POST** `/_apps/cleanup` - Stop and cleanup all running apps

### Proxy Endpoints

- **GET** `/apps/` - Redirect to first default app (if configured)
- **ALL** `/apps/{slug}/` - Proxy to app root
- **ALL** `/apps/{slug}/{path:path}` - Proxy HTTP requests to the app
- **WebSocket** `/apps/{slug}/stream` - Proxy WebSocket connections
- **WebSocket** `/apps/{slug}/_stcore/stream` - Proxy Streamlit core WebSocket
- **WebSocket** `/apps/{slug}/{ws_path:path}` - Generic WebSocket proxy

## Configuration

Environment variables:

- `ROOT_PATH` - Root directory for resolving relative paths (default: current working directory)
- `APP_REGISTRY_PATH` - Path to app registry JSON file (default: `app_registry.json`, relative to ROOT_PATH)
- `STARTING_PORT` - Starting port for Streamlit apps (default: `8503`)
- `MAX_PORT` - Maximum port for Streamlit apps (default: `8550`)

### Path Resolution

The proxy supports both absolute and relative paths:

- **Absolute paths**: Used as-is (e.g., `/home/user/apps/my_app.py`)
- **Relative paths**: Resolved relative to `ROOT_PATH` (e.g., `apps/my_app.py` → `{ROOT_PATH}/apps/my_app.py`)

This makes it easy to deploy in different environments (local, Docker, production) without changing app configurations.

## App Registration Format

```json
{
"name": "My Streamlit App",
"slug": "my-app",
"path": "/absolute/path/to/app.py",
"desired_port": 8503,
"run_by_default": false
}
```

### Fields

- **name** (required): Display name of the application
- **slug** (required): Unique identifier used in URLs (e.g., `/apps/{slug}/`)
- **path** (required): Path to the Streamlit app file (absolute or relative to ROOT_PATH)
- **desired_port** (optional): Preferred port for the app (auto-assigned if not specified)
- **run_by_default** (optional): If `true`, app will auto-start when proxy starts (default: `false`)

### Default Apps

Apps with `run_by_default: true` will:
1. Auto-start when the proxy server starts
2. Be accessible at the root `/apps/` path (first default app gets priority)

Example:
```json
{
"name": "Main Dashboard",
"slug": "dashboard",
"path": "apps/dashboard.py",
"run_by_default": true
}
```

Access at: `http://localhost:8000/apps/` → redirects to `http://localhost:8000/apps/dashboard/`

## WebSocket Support

This proxy provides full WebSocket support for Streamlit's real-time features:

- ✅ Real-time widget updates
- ✅ Interactive plots and charts
- ✅ Live data streaming
- ✅ Session state synchronization
- ✅ Auto-rerun functionality

## Example Usage

See the `apps/test_app.py` for a sample Streamlit application that demonstrates WebSocket functionality.

## Development

```bash
# Install development dependencies
uv sync

# Run in development mode with auto-reload
uvicorn main:app --reload --host 0.0.0.0 --port 8000

# Or with uv
uv run uvicorn main:app --reload

# Format code
uv run black .
uv run isort .

# Type checking
uv run mypy streamlit_proxy

# Linting
uv run ruff check .

# Build package
uv build
```

## Architecture

```
┌─────────────────┐ HTTP/WS ┌─────────────────┐
│ Client │ ◄─────────────► │ FastAPI Proxy │
│ Browser │ │ (Port 8000) │
└─────────────────┘ └─────────────────┘

│ HTTP/WS Proxy

┌─────────────────┐
│ Streamlit Apps │
│ (Ports 8503+) │
│ │
│ ┌──────────┐ │
│ │ App 1 │ │
│ │ App 2 │ │
│ │ App N... │ │
│ └──────────┘ │
└─────────────────┘
```

## Advanced Usage

### Programmatic App Management

```python
from streamlit_proxy.manager_service import AppControllerService
from streamlit_proxy.models import AppConfig

# Create service instance
controller = AppControllerService()

# Register an app programmatically
app_config = AppConfig(
name="Analytics Dashboard",
slug="analytics",
path="apps/analytics.py",
run_by_default=True
)
controller.register_app(app_config)

# Start the app
await controller.start_app("analytics")

# Check status
status = controller.get_app_status("analytics")
print(status)

# Stop the app
await controller.stop_app("analytics")
```

### Custom Streamlit Command

By default, the proxy uses `python -m streamlit` to run apps. You can customize this by subclassing `AppService`:

```python
from streamlit_proxy.services import AppService

class CustomAppService(AppService):
def _get_streamlit_command(self) -> list[str]:
# Use a specific Python version or virtual environment
return ["/path/to/venv/bin/python", "-m", "streamlit"]
```

### Cleanup Handlers

If integrating into an existing FastAPI app, you may want to handle cleanup:

```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from streamlit_proxy import integrate_streamlit_proxy
from streamlit_proxy.main import cleanup_running_apps

@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
yield
# Shutdown - cleanup Streamlit apps
await cleanup_running_apps()

app = FastAPI(lifespan=lifespan)
integrate_streamlit_proxy(app)
```

## Troubleshooting

### Apps not starting

1. Check that the path to your Streamlit app is correct
2. Verify the port range is available (STARTING_PORT to MAX_PORT)
3. Ensure Streamlit is installed: `pip install streamlit`
4. Check logs for specific error messages

### WebSocket connection issues

1. Ensure your reverse proxy (if any) supports WebSocket connections
2. Check that CORS middleware is properly configured
3. Verify firewall rules allow WebSocket connections

### Port conflicts

If you get port conflicts, adjust the port range:
```bash
export STARTING_PORT=9000
export MAX_PORT=9100
streamlit-proxy
```

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.