A real-time cryptocurrency price dashboard with a Bloomberg-terminal aesthetic. Live prices, high/low ranges, interactive charts, relative-performance overlays, multi-timezone clocks, and CSV/JSON export — built on a clean-architecture Rust backend and an Astro + React frontend.
🚧 Work in progress. The core feature set is complete and functional, but this project is not finished. More cryptocurrencies will be added (currently Bitcoin, Litecoin, Solana, and Ethereum), along with further enhancements. See Roadmap.
- Live spot prices for BTC, LTC, SOL, ETH in USD & EUR
- Real-time updates over WebSockets — prices flash green/red as they move, no refresh
- 24h percent change indicators on each price
- High / Low range with day / week / month tabs and a computed spread %
- Per-coin price charts — a 2×2 grid, each auto-scaled
- Relative performance overlay — all coins normalized to % change on one axis
- Multi-timezone clocks — New York, London, Tokyo, Madrid, ticking live
- Data export — download any coin's price series as CSV or JSON
- Dark / light theme toggle with an amber accent, persisted across sessions
Backend
- Rust with Axum (web framework)
- SQLx with PostgreSQL, over TLS 1.3 (rustls)
- Tokio async runtime
- Price data from the CoinGecko API
- Clean / hexagonal architecture (domain · application · infrastructure · presentation)
Frontend
- Astro with React islands
- Tailwind CSS v4
- Recharts for charting
- TypeScript, mirroring the backend's clean-architecture layering
Both backend and frontend follow the same hexagonal pattern — inner layers know nothing of outer ones; dependencies point inward through interfaces ("ports").
src/
├── domain/ # entities — Coin, Currency, Price, Period
├── application/ # ports (traits) + use cases
├── infrastructure/
│ ├── persistence/ # PostgreSQL via sqlx (TLS)
│ ├── external/ # CoinGecko HTTP client
│ └── scheduler/ # polling loop + broadcast
└── presentation/ # Axum handlers, router, WebSocket, DTOs
frontend/src/
├── domain/ # types mirroring the backend
├── application/ # ports + use cases
├── infrastructure/
│ ├── http/ # fetch-based gateway
│ ├── websocket/ # live price stream
│ └── container.ts # composition root (singleton)
└── presentation/ # React components + hooks + Astro layouts
The backend polls CoinGecko on an interval, stores snapshots in PostgreSQL, and broadcasts each batch to WebSocket subscribers. The frontend reads via REST for the initial load and subscribes to the WebSocket for live updates — components depend only on use cases, never on fetch or the API URL directly.
The system runs as three containers behind a single origin: nginx serves the static frontend and reverse-proxies /api (REST and WebSocket) to the Rust backend, which polls CoinGecko and persists to PostgreSQL.
flowchart LR
CG["CoinGecko API"]
Browser(["User's browser"])
subgraph host["Docker host — docker compose"]
direction LR
subgraph FE["frontend · nginx :3000"]
STATIC["Astro static build"]
PROXY["nginx /api proxy"]
end
subgraph BE["backend · Rust + Axum :8080"]
POLL["polling loop"]
BCAST["broadcast channel"]
HTTP["REST + WebSocket"]
end
DB[("PostgreSQL :5432 · TLS")]
end
CG -->|HTTPS poll| POLL
POLL -->|save prices| DB
POLL -->|price batch| BCAST
BCAST -->|push| HTTP
HTTP -->|queries| DB
Browser -->|load page| STATIC
Browser -->|api calls| PROXY
PROXY -->|proxy to backend| HTTP
And the runtime flow of a single live update, from poll to browser:
sequenceDiagram
autonumber
actor U as Browser
participant N as nginx
participant API as Backend
participant DB as PostgreSQL
participant CG as CoinGecko
Note over U,DB: Initial load (REST)
U->>N: GET /api/prices/latest
N->>API: proxy_pass
API->>DB: SELECT latest
DB-->>API: rows
API-->>U: prices (JSON)
Note over U,API: Live updates (WebSocket)
U->>N: connect /api/ws
N->>API: upgrade
loop every POLL_INTERVAL_SECONDS
API->>CG: fetch quotes
CG-->>API: JSON
API->>DB: INSERT prices
API-->>U: broadcast over WS
end
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health |
Liveness check |
GET |
/api/prices/latest |
Latest price for every coin/currency |
GET |
/api/prices/extremes?coin=¤cy=&period= |
Highest & lowest over a period |
GET |
/api/prices/series?coin=¤cy=&period= |
Time-series for charts |
GET |
/api/prices/export?coin=¤cy=&period= |
Download price series as CSV |
WS |
/api/ws |
Real-time price broadcast |
coin ∈ BTC, LTC, SOL, ETH · currency ∈ USD, EUR · period ∈ day, week, month
The fastest way to run the whole stack — PostgreSQL (TLS), the Rust backend, and the Astro frontend — together.
- Docker with the Compose plugin (Docker Desktop includes both)
git clone https://github.com/zr00t-1001/crypto-dashboard.git
cd crypto-dashboard
docker compose up --buildThen open http://localhost:3000.
flowchart TD
A["git clone the repo"] --> B["cd crypto-dashboard"]
B --> C["docker compose up --build"]
C --> D["containers start<br/>db · backend · frontend"]
D --> E["open localhost:3000"]
That one command:
- builds a TLS-enabled PostgreSQL image and creates the schema on first run,
- compiles the Rust backend in release mode and starts polling CoinGecko,
- builds the static frontend and serves it via nginx, which reverse-proxies
/api(REST and WebSocket) to the backend — so the browser uses a single origin, with no CORS and no hardcoded backend host.
Stop with Ctrl+C. To remove the containers and wipe the database volume:
docker compose down -vDefaults run out of the box. To override secrets or ports, copy the example and pass it explicitly:
cp .env.docker.example .env.docker
# edit .env.docker — at minimum set a real POSTGRES_PASSWORD
docker compose --env-file .env.docker up --build| Variable | Description | Default |
|---|---|---|
POSTGRES_PASSWORD |
Database password | changeme_dev_password |
POSTGRES_USER |
Database user | postgres |
POSTGRES_DB |
Database name | crypto_dashboard |
POLL_INTERVAL_SECONDS |
CoinGecko poll interval | 60 |
FRONTEND_PORT |
Host port for the dashboard | 3000 |
BACKEND_PORT |
Host port for the API | 8080 |
RUST_LOG |
Backend log level | info |
TLS note: the bundled Postgres image uses a self-signed certificate and the backend connects with
DB_SSL_MODE=require(encrypted, certificate not verified). For production, supply a CA-signed certificate and switch toverify-full.
| Service | What it is | Host port |
|---|---|---|
frontend |
nginx serving the Astro build + /api proxy |
3000 → 80 |
backend |
Rust / Axum API, poller, WebSocket | 8080 |
db |
PostgreSQL 17 with TLS | internal |
- Rust (stable)
- Node.js 18+ (20+ recommended)
- PostgreSQL with SSL enabled
Create the database and enable SSL on your PostgreSQL server (generate a self-signed cert for local development and set ssl = on in postgresql.conf).
CREATE DATABASE crypto_dashboard;Create a .env file in the project root:
DATABASE_URL=postgres://postgres:yourpassword@localhost:5432/crypto_dashboard
DB_MAX_CONNECTIONS=5
DB_SSL_MODE=require
DB_CA_CERT_PATH=
POLL_INTERVAL_SECONDS=60Run the migrations and start the server:
sqlx migrate run
cargo runThe backend starts on http://127.0.0.1:8080, begins polling CoinGecko, and serves the API.
cd frontend
npm install
npm run devThe dashboard is available at http://localhost:4321.
Run both the backend and frontend simultaneously. CORS is configured to allow the frontend's dev origin.
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | — (required) |
DB_MAX_CONNECTIONS |
Connection pool size | 5 |
DB_SSL_MODE |
require or verify-full |
require |
DB_CA_CERT_PATH |
CA cert path (required for verify-full) |
— |
POLL_INTERVAL_SECONDS |
How often to poll CoinGecko | 300 |
TLS: The backend connects to PostgreSQL over TLS. require encrypts the connection; verify-full additionally verifies the server certificate against a CA (set DB_CA_CERT_PATH). Switching modes is a .env change — no code edits.
This project is actively being developed. Planned additions:
- More cryptocurrencies — expand beyond the current four coins
- 24h volume and market-cap indicators
- Configurable / user-selectable coins
- Excel (
.xlsx) export -
verify-fullTLS in production - Deployment guide
- Additional timezones / configurable clocks
This project is provided as-is for educational and personal use.
Price data provided by CoinGecko. Built with Rust, Axum, Astro, and Recharts.