FHIR data that updates itself.
FHIR is the standard way healthcare software shares patient data. ReFHIR is an open-source FHIR server that keeps your query results live: ask once, and the new answer arrives whenever the data changes.
-
0 active: -The watcher starts and asks once. -
1 active: AnnAnn is created. -
2 active: Ann, BeeBee is created. -
1 active: BeeAnn is deactivated, so she leaves the result. -
3 active: Bee, Cho, DeeCho and Dee are created in one transaction, and arrive as one step.
The problem
Data changes. Your screen should too.
Plain query
Live query
Try it
Change the record. Watch both screens.
A plain FHIR query
GET /MedicationRequest?patient=Patient/123&status=active - Metformin 500 mg
- Lisinopril 10 mg
- Atorvastatin 20 mg
True when it was fetched.
A ReFHIR live query
the same query, kept open - Metformin 500 mg
- Lisinopril 10 mg
- Atorvastatin 20 mg
Kept current by the server.
Both screens start with the same list.
Compare
Queries, not events.
An event says that something changed. A live query says what the answer is now, pushed over one WebSocket.
| What happens | Plain queryask again | SubscriptionFHIR’s notifications: one message per record | Live querythe whole answer |
|---|---|---|---|
| You ask | 0 active | subscribed | 0 active |
| Ann is created | nothing | Ann | 1 active: Ann |
| Bee is created | nothing | Bee | 2 active: Ann, Bee |
| Ann leaves the list | nothing | nothing sent | 1 active: Bee |
| Cho and Dee, one transaction | nothing | ChoDee | 3 active: Bee, Cho, Dee |
How it works
A write wakes only the queries it can change.
- A write commits
- Only queries that read it wake
- Each gets the whole new answer
PUT /MedicationRequest/456 commits
Observation?patient=Patient/123 Not touched MedicationRequest?patient=Patient/123&status=active Re-runs. New answer pushed. Encounter?patient=Patient/123&status=in-progress Not touched Patient?name=ann Not touched Run it
Run it. Watch it.
1Start a server
git clone https://github.com/hai-hq/refhir.git
cd refhir
docker compose up --build 2Change data. Watch it print.
0 active: -
1 active: Ann
2 active: Ann, Bee
1 active: Bee
3 active: Bee, Cho, Dee 3Use it from your code
import { dial } from "@haihq/refhir";
const client = await dial("http://127.0.0.1:8080");
const watch = await client.search("Patient", { active: "true" });
console.log(watch.latest()); // the current matches
console.log(await watch.next()); // the next committed result import { useFHIRQuery } from "@haihq/refhir/react";
function PatientList() {
const { data, loading, error } = useFHIRQuery({
baseURL: "http://127.0.0.1:8080",
resourceType: "Patient",
params: { active: "true" },
runtime: "browser",
});
if (loading) return null;
if (error) return <pre>{error.message}</pre>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
} package main
import (
"context"
"encoding/json"
"log"
"github.com/hai-hq/refhir/clients/go"
)
func main() {
ctx := context.Background()
client, err := refhir.Dial(ctx, "http://127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
defer client.Close()
patient := json.RawMessage(`{
"resourceType": "Patient",
"active": true,
"name": [{"family": "Ann"}]
}`)
_, err = client.Create(ctx, "Patient", patient)
if err != nil {
log.Fatal(err)
}
watch, err := client.Search(ctx, "Patient", map[string]string{"active": "true"})
if err != nil {
log.Fatal(err)
}
defer watch.Close()
log.Printf("current: %s", watch.Latest())
// Blocks until another client (or this one) commits a change that affects the search.
updated, err := watch.Next(ctx)
if err != nil {
log.Fatal(err)
}
log.Printf("updated: %s", updated)
} GET /Observation?patient=Patient/123&status=final <- a snapshot, as with any FHIR server
ws://host/sync subscribe: Observation?patient=Patient/123&status=final
<- the matches now, then a new result whenever a commit changes them For agents
Live context for AI agents.
- 1 Read a live query keeps it current
- 2 Reason seconds or minutes pass
- 3 Check did the result change?
- 4 Write refused if the record moved
agent reads 1 active medication(s) and starts reasoning
context changed: now 0 active medication(s), so the agent re-plans
write refused: If-Match 12 does not match version 13 What is inside
A real FHIR server underneath.
- FHIR R4
- Search
- Transactions
- Bulk
- GraphQL
- Subscriptions
- SMART sign-in
- Every version kept
- Validation
- SQLite or Postgres
- Docker, Helm, binary
- Standby and replicas
- Audit trail
Ask once. Stay current.
Open source under Apache-2.0, from HAIHQ, a nonprofit. Contribute Open an issue Report a vulnerability