This is a submission for the Redis AI Challenge: Beyond the Cache.
What I Built
A real-time patient triage dashboard that uses Redis 8 as the primary data platform. It ingests vitals (HR/SpO₂/BP) via Redis Streams, persists the latest state per patient in Hashes, indexes snapshots with RediSearch for instant querying, publishes alerts with Pub/Sub, stores adjustable alert thresholds in RedisJSON, and renders 24-hour trends from RedisTimeSeries. The React/Next.js UI is real-time (WebSocket), offline-first (IndexedDB hydration), supports threshold configuration (persisted in Redis), and provides a drill-down panel with a per-patient sparkline (via TS.RANGE).
Key properties:
- Primary DB: No external database; Redis is the system of record for runtime state + settings.
- Search & Rules: RediSearch powers numeric queries for “critical” patients (low SpO₂ / high HR), pulled by a watcher that publishes alerts.
- Streams + Pub/Sub: Streams for ingestion/fan-out; Pub/Sub for real-time alert fan-out to the browser.
- Time series: Per-patient SpO₂ time series with 24h retention for drill-down trends.
- UX: Progressive skeletons, dark mode, drill-down, offline cache, threshold modal (live-reconfigures the watcher).
Demo
Github:
VitalSense Hackathon Project
A real-time health data pipeline using Redis Streams, RedisJSON, RediSearch Pub/Sub, and a beautiful Tailwind/Next.js dashboard.
Prerequisites
-
Node.js v16+
-
Python 3 (for the static dashboard, optional)
-
Redis 8 instance with modules:
- RedisJSON
- Redis Streams
- RediSearch
- (Optional) RedisAI
-
Environment variable
export REDIS_URL="redis://default:<PASSWORD>@<HOST>:<PORT>"
1. Vitals Generator
Emits synthetic patient vitals into patients:stream
.
cd vital-sense-generator
npm install
npm start
You’ll see logs like:
Emitted <streamId> { patientId: 'P01', hr: 82, spo2: 95, sys: 120, dia: 80 }
2. Stream Consumer
Reads from patients:stream
, writes snapshots to patient:<ID>:snapshot
, and
acknowledges.
cd vital-sense-consumer
npm install
npm start
You’ll see:
✔ Consumer group "consumer-group" created on "patients:stream"
▶ Listening for new entries…
🗂 Got 1 msg(s) from stream "patients:stream"
✅ Processed <streamId> → patient:P01:snapshot { hr: '82', spo2: '95', sys: '120', dia: '80' }
3. Index Creation (RediSearch)
Drop & recreate the snapshots index to cover all…
Demo:
How I Used Redis 8
Data ingestion (Streams)
XADD patients:stream * id P01 heart_rate 82 spo2 95 bp_systolic 120 bp_diastolic 80
- Consumer group (
XGROUP CREATE / XREADGROUP
) provides reliable processing and back-pressure.
Primary state (Hashes)
- Latest snapshot per patient in Hash:
HSET patient:P01:snapshot hr 82 spo2 95 sys 120 dia 80 timestamp <iso>
No external DB; Redis holds the live source of truth.
Indexed queries (RediSearch)
FT.CREATE idx:snapshots ON HASH PREFIX 1 "patient:" SCHEMA hr NUMERIC SORTABLE spo2 NUMERIC SORTABLE sys NUMERIC dia NUMERIC timestamp TAG
- Query examples used by the watcher/UI:
- Low oxygen:
@spo2:[-inf 92]
- High HR:
@hr:[120 +inf]
- Combined:
(@spo2:[-inf 92]) | (@hr:[120 +inf])
- Event fan-out (Pub/Sub)
- Watcher runs a periodic RediSearch query, publishes critical hits to alerts:critical.
- A Node WS proxy subscribes and forwards JSON to browser WebSockets.
Time series analytics (RedisTimeSeries)
Per-patient SpO₂:
TS.CREATE ts:patient:Pxx:spo2 RETENTION 86400000 LABELS patient Pxx
TS.ADD ts:patient:Pxx:spo2 <ts> <spo2>
- Drill-down:
TS.RANGE ts:patient:Pxx:spo2 <now-24h> <now>
- Dynamic configuration (RedisJSON)
Alert thresholds live in JSON:
JSON.SET settings:thresholds . '{"spo2":92,"hr":100}'
- Next API
GET/PATCH /api/settings/thresholds
reads/writes this key. - Watcher reloads thresholds each loop → instant effect.
Team Submissions:
Devin Nicholson
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.