Actix Web REST API template with PostgreSQL + sqlx, and WebSocket.
handler/-> API responses (status code + body)receiver/-> POST payload (deserialize + validation)models/-> query result structsutility/-> router, DB pool, response envelopesmigrations/-> DB schema, applied automatically at startup (sqlx migrate)ws.rs-> WebSocket actor hub (one actor per connection)
cp .env.example .env # then set DATABASE_URL
cargo runEnv: DATABASE_URL (required), HOST/PORT (default 0.0.0.0:8080),
ALLOWED_ORIGINS (comma-separated, default http://localhost:8000), RUST_LOG (default info).
Migrations run once at startup and are tracked in the _sqlx_migrations table, so a
second start is a no-op.
| Method | URL | Description |
|---|---|---|
| GET | /api/health |
liveness check |
| GET | /api/list?page=1&limit=20 |
content list, paginated (page, limit 1-100, total) |
| POST | /api/list |
create content -> 201 + content_id |
| GET | /api/ws |
WebSocket upgrade, receives broadcast notifications |
{
"title": "Title",
"short": 0,
"number": 0,
"link": "https://...",
"parent": 0,
"sub": 0
}link, parent, sub, and number are optional; the legacy key parrent is still
accepted as an alias for parent. Unknown fields are rejected.
Connect to ws://<host>:<port>/api/ws (use wss:// behind TLS).
On connect the server sends a hello frame:
{ "event": "connected", "session_id": "6f1c.. }Client - server messages:
{"type":"ping"}-> replies{"event":"pong"}- any other text -> echoed back as
{"event":"echo","data":.."}
Server - client broadcast, sent to every connected client whenever a content row is
created via POST /api/list:
{
"event": "new_content",
"content_id": 1,
"title": "Title",
"message": "New content created",
"timestamp": "2026-10-06T07:57:20.665088+00:00"
}The server pings every 30s as a heartbeat. Clients whose mailbox is closed or full are dropped from the hub automatically on the next broadcast.
const ws = new WebSocket("ws://localhost:8080/api/ws");
ws.onmessage = (e) => console.log(JSON.parse(e.data));- validation failure ->
400with a per-field message map indata - create success ->
201 - server error -> generic
500; details are logged server-side only, never returned - JSON body capped at 32 KB