Queries and Patterns
Fire Arrow Server's GraphQL schema is automatically generated from the FHIR resource definitions. Every FHIR resource type gets three query entry points, each suited to different use cases.
The Three Query Types
For each resource type (using Patient as an example):
| Query | Purpose | Example |
|---|---|---|
Patient(id: "123") | Fetch a single resource by its logical ID | Detail views, editing |
PatientList(...) | Bounded search returning an array of resources | Simple lists, dropdowns |
PatientConnection(...) | Cursor-based pagination for large result sets | Infinite scroll, paged tables |
Single Resource
Fetch one resource by ID:
curl -X POST http://localhost:8080/fhir/\$graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"query": "{ Patient(id: \"123\") { id name { family given } birthDate gender } }"
}'
Response:
{
"data": {
"Patient": {
"id": "123",
"name": [{ "family": "Smith", "given": ["Jane"] }],
"birthDate": "1990-01-01",
"gender": "female"
}
}
}
Bounded List
PatientList returns an array of matching resources. Use search parameters to filter:
curl -X POST http://localhost:8080/fhir/\$graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"query": "{ PatientList(name: \"Smith\", _count: 10) { id name { family given } } }"
}'
Use _offset for simple pagination:
{
PatientList(name: "Smith", _count: 10, _offset: 20) {
id
name { family given }
}
}
Cursor-Based Pagination
PatientConnection provides cursor-based pagination, which is more efficient for large datasets:
{
PatientConnection(name: "Smith", _count: 10) {
count
edges {
node {
id
name { family given }
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
}
}
}
To fetch the next page, pass the cursor of the last edge as _cursor:
{
PatientConnection(name: "Smith", _count: 10, _cursor: "WzEyM10=") {
edges {
node { id name { family given } }
cursor
}
pageInfo { hasNextPage }
}
}
Fire Arrow Server uses stateless cursors -- cursor values encode the position directly rather than maintaining server-side state. For this to work correctly, include Cache-Control: no-store in your requests to prevent caching from returning stale cursor pages:
curl -X POST http://localhost:8080/fhir/\$graphql \
-H "Content-Type: application/json" \
-H "Cache-Control: no-store" \
-H "Authorization: Bearer <your-token>" \
-d '{"query": "{ PatientConnection(_count: 10) { edges { node { id } cursor } pageInfo { hasNextPage } } }"}'
Search Parameters
Search parameters in GraphQL use underscores instead of the hyphens used in FHIR REST search parameters:
| FHIR REST Parameter | GraphQL Parameter |
|---|---|
general-practitioner | general_practitioner |
birth-date | birth_date |
address-city | address_city |
Special parameters:
| Parameter | Purpose |
|---|---|
_count | Maximum number of results to return |
_offset | Skip N results (for *List queries) |
_cursor | Resume from cursor (for *Connection queries) |
_sort | Sort field (prefix with - for descending) |
_text | Full-text search on resource narrative |
_content | Full-text search on resource content |
The _text and _content parameters require full-text search to be enabled in your configuration:
hapi:
fhir:
search_index_full_text_enabled: true
Reference Resolution
One of GraphQL's biggest advantages is resolving FHIR references in a single query. Use the reference field with inline fragments to access the referenced resource:
{
ObservationList(patient: "123") {
id
code { text }
subject {
reference {
resource {
... on Patient {
id
name { family given }
}
}
}
}
}
}
This eliminates the need for follow-up requests to fetch referenced resources -- everything comes back in one response.
Reverse References (Nested Resource Lists)
To fetch resources that reference a given resource (reverse direction), use the *List fields with the _reference parameter:
{
Patient(id: "123") {
id
name { family given }
ObservationList(_reference: "subject") {
id
code { text }
valueQuantity { value unit }
}
ConditionList(_reference: "subject") {
id
code { text }
clinicalStatus { coding { code } }
}
}
}
This fetches a patient along with all their observations and conditions in a single request, using _reference to specify which reference field on the child resource points back to the patient.
Complete Examples
Patient with Observations and Conditions
curl -X POST http://localhost:8080/fhir/\$graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"query": "{ Patient(id: \"123\") { id name { family given } birthDate gender telecom { system value } ObservationList(_reference: \"subject\") { id code { coding { system code display } } valueQuantity { value unit } effectiveDateTime } ConditionList(_reference: \"subject\") { id code { text } clinicalStatus { coding { code } } onsetDateTime } } }"
}'
Practitioner's Patient List with Encounters
curl -X POST http://localhost:8080/fhir/\$graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"query": "{ PatientList(general_practitioner: \"Practitioner/456\", _count: 20, _sort: \"family\") { id name { family given } birthDate EncounterList(_reference: \"subject\", _count: 5, _sort: \"-date\") { id status class { code } period { start end } } } }"
}'
Search with Full-Text and Pagination
curl -X POST http://localhost:8080/fhir/\$graphql \
-H "Content-Type: application/json" \
-H "Cache-Control: no-store" \
-H "Authorization: Bearer <your-token>" \
-d '{
"query": "{ PatientConnection(_content: \"diabetes\", _count: 10) { count edges { node { id name { family given } } cursor } pageInfo { hasNextPage } } }"
}'
CarePlan with Activities and Tasks
curl -X POST http://localhost:8080/fhir/\$graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{
"query": "{ CarePlan(id: \"789\") { id status subject { reference { resource { ... on Patient { id name { family given } } } } } activity { detail { description scheduledTiming { repeat { frequency period periodUnit } } } } TaskList(_reference: \"based-on\", _sort: \"-authored-on\") { id status code { text } executionPeriod { start end } } } }"
}'