A compact, production-minded realtime team chat (a mini Slack/Telegram) built
from scratch as a reference implementation. FastAPI serves both a JSON REST API
and a WebSocket hub; a dependency-light SQLite layer (stdlib sqlite3) stores
users, channels and messages; the frontend is a zero-build vanilla-JS PWA.
- JWT auth — register / login, passwords hashed with
pbkdf2_hmac(stdlib), tokens signed withexp. - Channels — list and create, all behind auth.
- Messages — history with keyset pagination (
limit/before), send endpoint. - Realtime WebSocket — clients authenticate by token, subscribe to channels, and receive new messages instantly via a fan-out hub.
- PWA frontend — installable app shell cached by a service worker for offline-friendly loading; HTML rendered XSS-safe via
textContent. - Tested — pure
pytest, including real WebSocket delivery tests, with no external services required.
+-------------------------------------------+
Browser | FastAPI app |
(PWA + WS) | |
+--------+ | REST routers ConnectionManager |
| app.js | <--+--> /api/auth --> (hub on app.state) |
| + | | /api/channels | broadcast per |
| sw.js | <--+--> /api/messages -----+ channel_id |
+--------+ | /ws (token auth) <-+ |
^ | | |
| | v |
+--WS----+ SQLite (sqlite3) |
| users / channels / messages |
+-------------------------------------------+
A REST POST /messages writes to SQLite then await hub.broadcast(...), which
pushes a JSON event to every WebSocket subscribed to that channel.
| Decision | Why |
|---|---|
Env-only config (os.getenv) |
No secrets in code; SECRET_KEY defaults to a loud placeholder; tests override via env. |
| Parameterized SQL everywhere | No string formatting in queries -> injection-safe by construction. |
WS auth via ?token=<JWT> |
Browsers can't set headers on WebSocket, so the token rides in the query and is verified before accept(). Trade-off: a query token can surface in proxy/access logs — mitigated by a short token TTL; passing it via Sec-WebSocket-Protocol is the hardening step (roadmap). |
pbkdf2_hmac for passwords |
Salted + slow hashing from the stdlib, no native build dependency. |
| Channels public within the deployment | Any authenticated user may read and subscribe to any channel. Per-channel membership/ACL is intentionally out of scope for this reference implementation (see roadmap) — it's a known limitation, not an oversight. |
Singleton hub on app.state |
One in-process ConnectionManager; async broadcast awaited from async routes. |
| PWA offline shell | Service worker caches static assets, never API/WS traffic. |
| Pure pytest incl. WebSocket tests | TestClient.websocket_connect proves realtime delivery without external infra. |
- Python 3.11+, FastAPI, Uvicorn
- Standard-library
sqlite3andhashlib - PyJWT for tokens, Pydantic for schemas
- Vanilla JS / HTML / CSS PWA (no build step)
- pytest + pytest-asyncio
python -m venv .venv
.venv/Scripts/activate # Windows
# source .venv/bin/activate # macOS/Linux
pip install -r requirements.txt
cp .env.example .env # optional; sensible defaults otherwise
uvicorn server.app:app --reload
# open http://localhost:8000 -> register "alice", chat in #general
pytest -q # run the full suite (incl. WebSocket tests)Demo channels general and random are seeded on first start. Register two
users (e.g. alice, bob) in two browser tabs to watch realtime delivery.
Out of scope for this reference implementation, but natural next steps:
- Per-channel membership & access control (ACL)
- Bot API (programmatic message senders)
- Web-push notifications
- Invite-based registration / org membership
- File uploads and attachments
- Horizontal scaling of the WS hub via Redis pub/sub
This is a from-scratch reference implementation. It contains no real data, secrets, domains, or credentials — only neutral demo values.
MIT © 2026 demonewgenij-maker