Skip to content

Latest commit

 

History

History
151 lines (113 loc) · 6.48 KB

File metadata and controls

151 lines (113 loc) · 6.48 KB

🌐 RADKit FastAPI Web Application Demo

RADKit version Python version

A fully functional FastAPI server with RADKit-powered API endpoints. This project demonstrates how to build a web application that connects to a RADKit service using a Direct connection, meaning the RADKit service must be network-reachable, have its RPC port configured, and have a Remote User set up. The FastAPI app authenticates against that service on startup and exposes REST endpoints to interact with the device inventory.

Project Structure

radkit-fastapi/
├── app/
│   ├── __init__.py
│   ├── main.py                  # App factory & lifespan (RADKit client init)
│   ├── api/
│   │   ├── __init__.py
│   │   └── v1/
│   │       ├── __init__.py
│   │       ├── router.py        # V1 router aggregator
│   │       └── routes/
│   │           ├── __init__.py
│   │           └── radkit.py    # RADKit API endpoints
│   └── core/
│       ├── __init__.py
│       ├── config.py            # App settings (pydantic-settings / .env)
│       └── radkit_client.py     # Shared RADKit service state
├── .env                         # Environment variables (credentials)
├── pyproject.toml               # Project metadata & dependencies (uv)
└── README.md

Setup & Run

1. Install uv

uv is a fast Python package and project manager. Install it once on your machine:

Platform Command
🍎🐧 macOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh
🪟 Windows (PowerShell) powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Or via Homebrew on macOS:

brew install uv

Verify the installation:

uv --version

2. Create the virtual environment and install all dependencies

From the project root, run:

uv sync

uv sync reads pyproject.toml, automatically creates a .venv with Python 3.13, and installs all dependencies — including the RADKit packages from https://radkit.cisco.com/pip and the FastAPI stack from PyPI — in a single step.

3. Configure environment variables

Copy the example env file and fill in your RADKit credentials:

cp .env.example .env   # or create .env manually

Required variables:

USER_ID=your@email.com
E2EE_VALIDATION_TOKEN=your-remote-user-token
RADKIT_SERVER_ADDRESS=your-radkit-server.example.com
RPC_PORT=your-radkit-server-port (default is 8181)

4. Run the server

uv run uvicorn app.main:app --reload

Note: The --reload flag watches for file changes and automatically restarts the server, including re-running the RADKit authentication handshake. This means you can update your code or .env credentials without stopping and redeploying the app manually.

Alternatively, activate the virtual environment first and then run commands directly:

Platform Activate command
🍎🐧 macOS / Linux source .venv/bin/activate
🪟 Windows (cmd) .venv\Scripts\activate.bat
🪟 Windows (PowerShell) .venv\Scripts\Activate.ps1
# After activation:
uvicorn app.main:app --reload

The app will be available at:

Interface URL
API http://127.0.0.1:8000
Swagger UI http://127.0.0.1:8000/docs
ReDoc http://127.0.0.1:8000/redoc

Endpoints

General

Method Path Description
GET / Root welcome message

RADKit (/api/v1/radkit)

Method Path Description
GET /api/v1/radkit/status RADKit service connectivity status and identity
GET /api/v1/radkit/devices List all devices in the RADKit inventory
POST /api/v1/radkit/devices/{device_name}/commands Execute any CLI command on a device
GET /api/v1/radkit/devices/{device_name}/show Execute a show command and return Genie-parsed output

How RADKit integrates with FastAPI

RADKit is integrated using FastAPI's lifespan handler (app/main.py), which runs setup and teardown logic around the entire application lifecycle. Inside the lifespan, Client.create() opens a RADKit context and immediately establishes a Direct connection to the RADKit service using credentials from the environment. The resulting service object is stored in a module-level variable (app/core/radkit_client.py) so every route can access it without passing it around explicitly. When the app shuts down, the context manager cleans up the connection and resets the global to None.

@asynccontextmanager
async def lifespan(app: FastAPI):
    with Client.create() as client:
        service = client.service_direct(
            username=settings.USER_ID,
            host=settings.RADKIT_SERVER_ADDRESS,
            port=settings.RPC_PORT,
            password=settings.E2EE_VALIDATION_TOKEN
        )
        radkit_state.radkit_service = service  # shared global used by all routes
        yield                                  # app runs here
    radkit_state.radkit_service = None         # cleanup on shutdown

app = FastAPI(lifespan=lifespan)

Key points to understand:

  • Client.create() is synchronous. It spawns a background thread with its own event loop dedicated to RADKit. This thread is separate from FastAPI's async event loop.
  • All RADKit calls must be synchronous. Route handlers that interact with RADKit must be regular def functions — not async def. Using async def for RADKit calls will cause deadlocks or unexpected behaviour.
  • Global state. The service object is stored in app/core/radkit_client.py as a module-level variable and imported by route modules wherever needed.
  • Single-process only. The ASGI runner (uvicorn) must not use multiprocessing workers (e.g. avoid --workers N with N > 1), as each process would have its own RADKit thread and independent authentication state.