Desktop App Arch: FastAPI + Web + pywebview⚓︎
A creative architecture for building desktop applications with shorter delivery time for a certain class of desktop tools.
Recently got a new way to build desktop applications. This is not a typical Qt desktop application. The core idea is:
- BE: local
FastAPIbackend service - FE: Web pages with HTML, CSS, and JavaScript
- Shell: open it inside
pywebviewto make it a desktop app.
The interesting part is that this is still a real installable desktop application, not “just a website in a browser”. In practice, the release pipeline can use:
GitHub Actionsbuilds the app.PyInstallerproduces the executable.Inno Setupcreates the corresponding installer.
Code structure⚓︎
The minimal structure looks like this:
project/
main.py # desktop launcher
app/
main.py # FastAPI entrypoint
modules/ # business logic for each miniapp
web/
home.html # home page
apps/ # individual miniapp pages
assets/ # icons, images, static files
Each layer has a very clear job:
main.py: starts the local service, then opens the desktop window.app/main.py: exposes routes, static assets, and JSON APIs.app/modules/*/service.py: contains rules, calculations, and validation.web/apps/*.html: handles presentation and user interaction only.
Runtime flow⚓︎
sequenceDiagram
actor User
participant Launcher as main.py
participant API as FastAPI / uvicorn
participant Shell as pywebview
participant UI as Web UI
participant Service as service.py
User->>Launcher: launch desktop app
Launcher->>API: start local server
API-->>Launcher: listen on 127.0.0.1:8005
Launcher->>Shell: create desktop window
Shell->>UI: load local page
User->>UI: interact with forms / buttons
UI->>API: call local HTTP API
API->>Service: run rules / calculation / validation
Service-->>API: return result
API-->>UI: return JSON response
UI-->>User: update the page
How it differs from Qt⚓︎
The main difference is not that both can build desktop software. The real difference is what sits at the center of the system.
flowchart LR
subgraph A["This architecture"]
direction TB
A1["Desktop launcher"]
A2["Web UI"]
A3["HTTP API"]
A4["Service layer"]
A5["Rules / calculations"]
A1 --> A2 --> A3 --> A4 --> A5
end
subgraph B["Qt architecture"]
direction TB
B1["QApplication / window"]
B2["Widgets / QML"]
B3["Signal-Slot"]
B4["UI-owned logic or controller"]
B5["Rules / calculations"]
B1 --> B2 --> B3 --> B4 --> B5
end
A1 ~~~ B1
A2 ~~~ B2
A3 ~~~ B3
A4 ~~~ B4
A5 ~~~ B5
| Dimension | This architecture | Qt architecture |
|---|---|---|
| UI layer | HTML / CSS / JS | QWidget / QML |
| Desktop shell | pywebview |
Qt itself |
| Business entry | HTTP API | signal-slot |
| Extension model | add page + API + service | add widget + event wiring |
| System center | local service | native UI |
In short:
- Qt: the window is the center.
- This approach: the service is the center.
Advantages⚓︎
This approach is especially useful when:
- Fast Delivery: building screens with Web technologies is often faster than building them with
Qt. - Easier iteration: UI changes can usually be made and tested more quickly with familiar HTML, CSS, and JavaScript workflows.
- Decoupled: the UI and logic are cleanly separated by HTTP APIs explicitly.
- Better extensibility: adding a new miniapp usually means adding one page, one API, and one service module.
- UI framework flexibility: the UI can be built with whatever Web framework or library best fits the project.
Disadvantages⚓︎
This architecture also comes with real trade-offs:
- More moving parts: the launcher, local server, Web UI, and packaging pipeline all have to work together.
- More complex debugging: problems may come from the desktop shell, frontend, backend, or the boundaries between them.
- API boundary overhead: UI-to-business communication goes through HTTP-style interfaces instead of direct in-process calls.
- Heavier startup: the application usually needs to initialize a local service before the UI becomes available.
- Less natural for native-heavy apps: if the product depends heavily on native desktop widgets or deep OS integration,
Qtcan still be a better fit.