---
name: deploy-posit-connect
description: Deploy a FastAPI backend (optionally serving a built SPA frontend) to Roche Posit Connect / rsconnect (rsconnect-pred.roche.com) with rsconnect-python. Use when packaging and pushing a Python web app to Connect, or wiring FastAPI to serve a Vite/React build as one deployable content.
---

# Deploy to Posit Connect (rsconnect)

Ship a FastAPI app (with or without a bundled SPA) to Roche Posit Connect using the
`rsconnect-python` CLI. Requires the **Roche network / VPN** to reach the Connect server.

## One-content architecture (API + UI together)
Serve the built frontend from FastAPI so a single Connect content covers both. Two things
matter for Connect's `/content/<guid>/` URL prefix:

1. **Relative asset paths** in the frontend build:
   - Vite: `base: "./"` in `vite.config.ts`.
   - API base: relative (`"api"`, not `"/api"`) so `fetch` resolves under the prefix.
2. **FastAPI serves the build** (static mount + SPA fallback):
```python
from pathlib import Path
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles

STATIC = Path(__file__).resolve().parent / "static"   # built SPA copied here
if STATIC.is_dir():
    app.mount("/assets", StaticFiles(directory=STATIC / "assets"), name="assets")
    @app.get("/{full_path:path}")                       # register AFTER /api routers
    def spa(full_path: str):
        cand = STATIC / full_path
        return FileResponse(cand if full_path and cand.is_file() else STATIC / "index.html")
```

## Build + stage the frontend (PowerShell, Windows)
```powershell
cd frontend; npm run build; cd ..
Remove-Item -Recurse -Force backend\app\static -ErrorAction SilentlyContinue
Copy-Item -Recurse frontend\dist backend\app\static
```

## Deploy
```powershell
pip install rsconnect-python

# Register the server once (nickname is chosen here — reuse it in --name below)
$env:CONNECT_API_KEY = "<API_KEY>"
rsconnect add --server https://rsconnect-pred.roche.com/ --name pred-bar --api-key $env:CONNECT_API_KEY

# Deploy the backend (which embeds the UI). PowerShell continuation = backtick `
rsconnect deploy fastapi backend `
  --entrypoint app.main:app `
  --name pred-bar `
  --title "My App" `
  --exclude ".venv" --exclude "**/__pycache__" --exclude ".env"
```
`requirements.txt` must sit at the deployed dir root (`backend/`); Connect installs it.

## Gotchas hit in practice
- **`Error: The nickname "X" does not exist`** → `--name` must match the nickname used in
  `rsconnect add`.
- **`Unable to include ...\.venv\bin\python ... Invalid argument`** → the virtualenv got
  bundled. Exclude it: `--exclude ".venv"`. Also exclude `.env` so **secrets don't ship**.
  Or add a `backend/.rscignore` with `.venv/`, `**/__pycache__/`, `.env`, `*.pyc`.
- **Warning: missing Python version constraint** → add `backend/.python-version` (e.g. `3.11`).
- Set runtime secrets (DB creds, API keys) in the Connect UI → content → **Vars**, never in
  the bundle. Verify with `.../content/<guid>/api/health`.

## After deploy checklist
- [ ] Frontend built with relative `base` and API base
- [ ] `dist` copied into `backend/app/static`
- [ ] `.venv` / `.env` excluded from the bundle
- [ ] Env vars set in Connect Vars
- [ ] `/api/health` responds under the content URL
