Skip to content

The big picture

Nessie is one FastAPI process. It answers the JSON API under /v1/… and also serves the UI's files straight from frontend/. The UI has no build step. The browser loads index.html, which loads js/main.js as an ES module, and the browser follows the imports from there. What you see in the editor is exactly what runs.

flowchart LR
  subgraph Browser
    UI["UI (frontend/)<br/>index.html + js/ modules"]
    SW["Service worker<br/>sw.js"]
    AUD["#lt;audio#gt; element"]
  end
  subgraph Server["Nessie server (app/)"]
    API["FastAPI routes<br/>app/api/"]
    SVC["Services and repositories<br/>app/services, app/repositories"]
    MPV["mpv subprocess<br/>(the server's speakers)"]
  end
  DB[("Postgres")]
  FILES[("Music files<br/>+ artwork store")]
  EXT["MusicBrainz, Wikidata,<br/>Fanart.tv, Anthropic, Lidarr"]
  UI -- "fetch /v1/..." --> SW --> API
  AUD -- "GET /v1/library/tracks/{id}/stream" --> SW
  API --> SVC --> DB
  SVC --> FILES
  SVC --> MPV
  SVC --> EXT

Two UIs share the same API and data:

  • / is the current UI (internally called v3), the subject of this guide.
  • /v2/ is the classic UI: one large script, frontend/v2/js/script_v2.js. It's kept working but isn't where new work goes.

Music can play in two places, and the UI treats them the same way:

  • The server's speakers. The server runs mpv and keeps the queue itself. The UI sends commands and polls the state.
  • This device. An <audio> element in the browser streams files from the server. The queue lives in the page.

FastAPI also generates interactive API docs at /docs, built from the same code this guide describes. Use them to try calls by hand.