FastAPI facade service for the Dataspot platform.
This guide is for users who want to use and test the Dataspot Facade API.
Official API Documentation: https://facade-api.datenkatalog.bs.ch/docs
Verify that you have the necessary permissions in Dataspot:
- Log in to Dataspot.
- Navigate to "Data Governance"
$\rightarrow$ "Personen"$\rightarrow$ select your own profile. - Check the section "Facade-API Berechtigungen".
- If the required permissions are not listed, please contact the DCC Data Competence Center at dcc@bs.ch.
The API is protected by JWT (JSON Web Tokens). To make requests, you must first obtain a token.
Step A: Get your Access Key (access_key)
- Log in to datenkatalog.bs.ch.
- Click on the User icon (head icon at the top right).
- Navigate to "Access Keys…" and create a new access key.
Step B: Exchange the Access Key for a JWT
- Call the
/v1/authendpoint using youraccess_keyto receive a short-lived JWT (this is usually done directly in code, e.g. in example.py). This JWT must be used in the Authorization header for all subsequent API calls.
Access to the API depends on your specific permissions within Dataspot.
| Functionality | Endpoint | Requirements |
|---|---|---|
| Run Query | /v1/queries/execute |
Requires the corresponding permission in Dataspot (granted by DCC). |
| Update lastUpdate | /v1/datasets/{id}/last-update |
1. Requires the corresponding permission in Dataspot. 2. Only the assigned "Data Steward Datenrelease" for that specific data product is authorized to perform this action. |
To verify that your permissions are correctly configured and the API is reachable, you can use the provided Python example script:
- Install httpx:
pip install httpx truststore(I used httpx==0.28.1 truststore==0.10.5, but newer versions are preferable) - Download example script: examples/python/example.py
- Execution: Run the script with
python example.py --access-key <your-access-key> - Result The result should be the number of posts in dataspot, e.g.
(got fresh JWT) [ { "count": 328 } ]
- mise (manages the toolchain, incl.
uvand Python)
Install the toolchain and dependencies:
mise install
uv syncRustrak is a self-hosted, Sentry-compatible
error tracker. compose.yml runs both Rustrak and this API:
cp .env.example .env # fill in values
docker compose up -dThis starts:
- Rustrak on
http://localhost:8080— the dashboard and its/api, with data persisted in a Docker volume. - dataspot-facade-api on
http://localhost:8000.
For production, compose.prod.yml also builds and runs this API (with
IS_PROD=true) alongside Rustrak:
cp .env.example .env # fill in values
docker compose -f compose.yml -f compose.prod.yml up -d --buildThe API is built from the local Dockerfile and served on
http://localhost:8000 (override with APP_PORT). The API port can be
overridden with APP_PORT, e.g. APP_PORT=9000 docker compose -f compose.yml -f compose.prod.yml up -d.
On first start Rustrak creates the admin user from RUSTRAK_SUPERUSER (format
email:password). SESSION_SECRET_KEY must be set — generate one with:
openssl rand -hex 32- Log in to the Rustrak dashboard (
http://localhost:8080). - Create a project and copy its DSN, e.g.
http://<key>@localhost:8080/1. - Set
RUSTRAK_DSN=<that-dsn>in.env. - Restart:
- Compose:
docker compose up -d --force-recreate app - Dev server (picks up the change on reload): just keep
mise run devrunning
- Compose:
The API is wired with sentry-sdk (src/dataspot_facade_api/app.py) and
reports four things to the Rustrak dashboard:
- Errors / issues — unhandled exceptions and any record at or above
RUSTRAK_EVENT_LEVEL(defaultERROR) become dashboard issues. - Logs — every log record at or above
RUSTRAK_LOG_LEVEL(defaultINFO) is forwarded to Rustrak's Logs view. All app logging uses the Pythonloggingstdlib, which the bundledLoggingIntegrationcaptures. - Performance / transactions — every HTTP request becomes a transaction
with its own timing, and handler/outbound calls are spans (driven by the
bundled FastAPI/Starlette integrations,
traces_sample_rateandprofiles_sample_rate). Handlers also attach context viasentry_sdk.set_tag(e.g.user,query_duration_ms). - Audit trail — every protected endpoint logs the acting user's email and the action taken (e.g. query execution, dataset lastUpdate updates), taken from the JWT payload the request already carries.
In production set
RUSTRAK_PUBLIC_URLto your public host so SDKs get a reachable DSN.
Set BASE_PATH (e.g. /fade-api) in .env and the API serves its routes,
docs and OpenAPI schema under that prefix:
# nginx: forward the prefix untouched — no extra config needed
location /fade-api/ {
proxy_pass http://app:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}If your proxy strips the prefix instead (e.g. proxy_pass http://app:8000/v1/), either start uvicorn with --root-path /fade-api or
set ROOT_PATH=/fade-api so generated doc URLs still carry the prefix.
The Rustrak dashboard is a SPA served from the root of its origin (base: '/'
in its Vite build, / router basepath) and the server has no base-path
setting. Give it its own hostname at the root:
server {
server_name rustrak.example.com;
location / {
proxy_pass http://rustrak:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Set RUSTRAK_PUBLIC_URL=https://rustrak.example.com (and
DASHBOARD_URL=https://rustrak.example.com for alert links) and
SSL_PROXY=true behind HTTPS. The facade API and Rustrak can then share one
host: this API on /fade-api, the Rustrak dashboard at /.
Copy the example env file and fill in the values:
cp .env.example .env| Variable | Required | Description |
|---|---|---|
MY_DATASPOT_TOKEN |
yes | Access key used to authenticate via /v1/auth and receive a JWT |
JWT_SECRET |
yes | Secret key for signing JWT tokens |
BASE_URL |
yes | Base URL of the Dataspot instance |
DATABASE_NAME |
yes | Database name in Dataspot |
BASE_PATH |
no | Base path the API is served under, e.g. /fade-api behind a reverse proxy. Defaults to empty (served at the root) |
ROOT_PATH |
no | ASGI root_path for when the proxy strips the prefix and uvicorn is not started with --root-path. Defaults to BASE_PATH |
IS_PROD |
no | Set to "true" to enable production mode (disables local CORS origins). Defaults to "false" |
DATASPOT_SERVICE_USER_ACCESS_KEY |
no | Backend service-user access key |
DATASPOT_TENANT_ID |
no | Azure AD / Entra ID tenant ID |
DATASPOT_CLIENT_ID |
no | Azure AD / Entra ID client ID |
DATASPOT_CLIENT_SECRET |
no | Azure AD / Entra ID client secret |
DATASPOT_EXPOSED_CLIENT_ID |
no | Azure AD / Entra ID exposed client ID |
| Variable | Default | Description |
|---|---|---|
RUSTRAK_DSN |
— | Sentry-compatible DSN. When set, enables error tracking, logging and performance monitoring |
RUSTRAK_LOG_LEVEL |
INFO |
Minimum level forwarded to Rustrak Logs (DEBUG / INFO / WARNING / ERROR) |
RUSTRAK_EVENT_LEVEL |
ERROR |
Records at or above this level also become dashboard issues |
RUSTRAK_DEBUG |
false |
Set "true" to enable the app's logger.debug(...) lines (raises LOG_LEVEL to DEBUG) |
To see debug records forwarded to Rustrak, set RUSTRAK_DEBUG=true (or
RUSTRAK_LOG_LEVEL=DEBUG).
Besides stdout and Rustrak, all log records are also written to a rotating log file (10 MB per file, 5 backups kept). The per-request audit entries (user email, action, status) land in the same file.
| Variable | Default | Description |
|---|---|---|
LOG_FILE |
/app/logs/app.log |
Path of the rotating log file. Falls back to stdout-only logging when the path is not writable (e.g. local dev). In Docker, mount a volume at /app/logs to persist logs |
LOG_LEVEL |
INFO |
Minimum level for stdout and the log file (DEBUG / INFO / WARNING / ERROR). RUSTRAK_DEBUG=true also raises this to DEBUG |
mise run devThis runs uv run fastapi dev, which starts the API with auto-reload and
serves the interactive docs at http://127.0.0.1:8000/docs.
mise run authThis task:
- Calls
/v1/authwithMY_DATASPOT_TOKENto obtain a JWT - Copies the JWT to your clipboard
- Opens a local docs page (port 8090) with the token pre-authorized in Swagger UI
The local docs server keeps running until you press Ctrl+C. Make sure the dev
server is already running (mise run dev) before using this task.