Searching and Querying Threads | Medplum
Search Query Cheat Sheet
| Goal | Query | Notes |
|---|---|---|
| All threads | Communication?part-of:missing=true |
Returns only thread headers |
| Messages in a thread | Communication?part-of=Communication/{id}&_sort=sent |
Sorted chronologically |
| Threads for a patient | Add &subject=Patient/{id} to any query above |
Filter by patient context |
| Threads I participate in | Add &recipient=Practitioner/{id} |
Header must list the creator in recipient; see note below and modeling tip |
| Active threads only | Add &status:not=completed,entered-in-error,stopped,unknown |
Exclude closed threads |
Query for All Threads
- Typescript
- CLI
- cURL
// Search reference: find all thread headers, sorted by most recently active
await medplum.searchResources('Communication', {
'part-of:missing': true,
'status:not': 'completed,entered-in-error,stopped,unknown',
_sort: '-_lastUpdated',
});
medplum get 'Communication?part-of:missing=true&status:not=completed,entered-in-error,stopped,unknown&_sort=-_lastUpdated'
curl 'https://api.medplum.com/fhir/R4/Communication?part-of:missing=true&status:not=completed,entered-in-error,stopped,unknown&_sort=-_lastUpdated' \
-H 'authorization: Bearer $ACCESS_TOKEN' \
-H 'content-type: application/fhir+json'
The :missing modifier finds Communication resources that have no partOf reference — these are thread headers. Adding status:not=completed filters to only active threads. Sorting by -_lastUpdated (descending) puts the most recently active threads first.
Query for Messages in a Thread
- Typescript
- CLI
- cURL
// Retrieve all messages in a thread, sorted chronologically
await medplum.searchResources('Communication', {
'part-of': `Communication/${threadHeader.id}`,
_sort: 'sent',
});
medplum get 'Communication?part-of=Communication/{threadHeaderId}&_sort=sent'
curl 'https://api.medplum.com/fhir/R4/Communication?part-of=Communication/{threadHeaderId}&_sort=sent' \
-H 'authorization: Bearer $ACCESS_TOKEN' \
-H 'content-type: application/fhir+json'
This retrieves all child messages for a given thread header, sorted chronologically by the sent timestamp.
Add Filters
Filter threads by patient:
- Typescript
- CLI
- cURL
// Filter threads to a specific patient
await medplum.searchResources('Communication', {
'part-of:missing': true,
subject: 'Patient/homer-simpson',
});
medplum get 'Communication?part-of:missing=true&subject=Patient/homer-simpson'
curl 'https://api.medplum.com/fhir/R4/Communication?part-of:missing=true&subject=Patient/homer-simpson' \
-H 'authorization: Bearer $ACCESS_TOKEN' \
-H 'content-type: application/fhir+json'
Filter by Current User
To show threads the current user participates in, filter by recipient:
- Typescript
- CLI
- cURL
// Filter to only the current user's active threads
await medplum.searchResources('Communication', {
'part-of:missing': true,
recipient: getReferenceString(profile),
'status:not': 'completed,entered-in-error,stopped,unknown',
});
medplum get 'Communication?part-of:missing=true&recipient=Practitioner/{id}&status:not=completed,entered-in-error,stopped,unknown'
curl 'https://api.medplum.com/fhir/R4/Communication?part-of:missing=true&recipient=Practitioner/{id}&status:not=completed,entered-in-error,stopped,unknown' \
-H 'authorization: Bearer $ACCESS_TOKEN' \
-H 'content-type: application/fhir+json'
This works when thread headers list all participants — including the thread creator — in recipient. The Medplum ThreadInbox component follows this convention: when a new thread is created, the sender is added to recipient alongside other participants, so a recipient-based query returns threads the user started and threads they were added to.
For other items to filter on, see the Communication Search Parameters.
Live Updates
To receive new messages in real time, subscribe to Communications in the current thread. WebSocket subscriptions may need to be enabled for your project (for example, the websocket-subscriptions feature). The subscription criteria for a specific thread:
Communication?part-of=Communication/{threadId}
When a notification arrives, append the new message to your local state. The notification Bundle contains a SubscriptionStatus entry and the new Communication — find it by resourceType rather than by position:
const threadId = 'example-thread-id';
const emitter = medplum.subscribeToCriteria(`Communication?part-of=Communication/${threadId}`);
emitter.addEventListener('message', (event) => {
const newMessage = event.payload.entry?.find((e) => e.resource?.resourceType === 'Communication')?.resource;
if (newMessage) {
console.log(newMessage);
}
});
If using @medplum/react, the useSubscription hook handles connection management:
useSubscription(
`Communication?part-of=Communication/${id}`,
(bundle: Bundle) => {
const newMessage = bundle.entry?.find(
(e) => e.resource?.resourceType === 'Communication'
)?.resource as Communication | undefined;
if (newMessage) {
// Show a toast or in-app notification for the new message
setMessages((prev) => [...prev, newMessage]);
}
}
);
See the useSubscription documentation for WebSocket connection setup and reconnection handling.