Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/dhanyasukumaran1/fhir_query_validator/llms.txt

Use this file to discover all available pages before exploring further.

FHIR R4 search goes well beyond simple parameter matching. Chained parameters let you filter resources by properties of referenced resources, modifiers change how string and token parameters are matched, and composite parameters let you express “and” semantics within a single parameter. All of these patterns have strict syntax rules that FHIRQueryValidator checks before a query ever reaches a server. This guide covers each pattern with working examples and explains common mistakes that pass a cursory look but fail validation.

Chained parameters

Chained parameters traverse a reference to filter by properties of the referenced resource. The chain uses dot notation: ResourceType?reference-param.target-param=value.
from fhir_query_validator import FHIRQueryValidator

validator = FHIRQueryValidator()

# Find Observations where the patient's family name is Smith
result = validator.validate_query("Observation?patient.name=Smith")
print(result.is_valid)  # True

# Find Observations where the patient was born after 1970
result = validator.validate_query("Observation?patient.birthdate=gt1970-01-01")
print(result.is_valid)  # True

# Multi-level chain: DiagnosticReport → performer (Organization) → name
result = validator.validate_query("DiagnosticReport?performer.name=LabCorp")
print(result.is_valid)  # True
The validator checks that the reference parameter exists on the source resource and that the target parameter exists on the referenced resource type. A chain like Observation?patient.unknownparam=x fails validation even though the syntax looks correct.

Common chained parameter mistakes

Only parameters of type reference can be chained. Using a string or token parameter as the root fails:
# INVALID: 'status' is a token parameter, not a reference
result = validator.validate_query("Observation?status.value=final")
print(result.is_valid)   # False
print(result.errors)     # ["'status' is not a reference parameter and cannot be chained"]
The validator resolves the target resource type and checks the parameter against it:
# INVALID: Patient does not have a 'diagnosis' search parameter
result = validator.validate_query("Observation?patient.diagnosis=diabetes")
print(result.is_valid)   # False

Search modifiers

Modifiers qualify how a search parameter value is matched. They are appended to the parameter name with a colon: parameter:modifier=value.
Match the full string with case-sensitive, accent-sensitive comparison. Without :exact, string searches are case-insensitive and accent-insensitive.
# Match patients whose family name is exactly "Smith" (not "smith" or "SMITH")
result = validator.validate_query("Patient?name:exact=Smith")
print(result.is_valid)  # True
Not every modifier applies to every parameter type. Applying :exact to a date or token parameter, or :contains to a number parameter, produces a validation error. The validator enforces modifier–type compatibility.

Comparison prefixes on date and number parameters

Date and quantity parameters accept a two-character prefix that specifies the comparison operator. The prefix is prepended directly to the value with no separator.
PrefixMeaningExample
eqEqual (default)birthdate=eq1990-01-01
neNot equalbirthdate=ne1990-01-01
ltLess thanbirthdate=lt2000-01-01
leLess than or equalbirthdate=le2000-01-01
gtGreater thanbirthdate=gt1980-01-01
geGreater than or equalbirthdate=ge1980-01-01
saStarts afterdate=sa2023-01-01
ebEnds beforedate=eb2024-01-01
apApproximatelyvalue-quantity=ap70|http://unitsofmeasure.org|kg
# Observations with a value quantity greater than 100 mg/dL
result = validator.validate_query(
    "Observation?value-quantity=gt100|http://unitsofmeasure.org|mg/dL"
)
print(result.is_valid)  # True

# Encounters that started after 2023-06-01
result = validator.validate_query("Encounter?date=sa2023-06-01")
print(result.is_valid)  # True
The sa (starts after) and eb (ends before) prefixes are specifically designed for period-typed parameters where you care about whether the range starts or ends relative to your search value, not just whether it overlaps.

Composite parameters

Composite parameters express an “and” condition between two sub-parameters within a single parameter value, using $ as the separator. They are used when you need both components to apply to the same occurrence of a repeating element.
# Find Observations with code 8302-2 AND value > 170 cm in the same component
result = validator.validate_query(
    "Observation?component-code-value-quantity=8302-2$gt170"
)
print(result.is_valid)  # True
Composite parameters are defined per resource in the FHIR specification. Not every pair of parameters can be combined with $. The validator checks that the composite parameter name is defined for the resource and that the sub-values match their expected types.

_include and _revinclude

_include tells the server to return related resources referenced by the matched resources. _revinclude returns resources that reference the matched resources. Both use the format ResourceType:search-parameter or ResourceType:search-parameter:target-type.
# Return MedicationRequests and their referenced Medication resources
result = validator.validate_query(
    "MedicationRequest?status=active&_include=MedicationRequest:medication"
)
print(result.is_valid)  # True

# Return Patients and any Observations that reference them
result = validator.validate_query(
    "Patient?name=Smith&_revinclude=Observation:patient"
)
print(result.is_valid)  # True

# Wildcard: include all referenced resources (use with caution)
result = validator.validate_query(
    "MedicationRequest?status=active&_include=MedicationRequest:*"
)
print(result.is_valid)   # True
print(result.warnings)   # ["Wildcard _include may return a very large bundle"]
Wildcard _include and _revinclude (using *) can return enormous bundles. The validator raises a warning rather than an error, but most FHIR servers impose their own limits that may truncate results silently.

Validate queries with multiple parameters

Multiple search parameters are joined with &. The validator checks each parameter independently and accumulates errors and warnings from all of them.
query = (
    "Observation"
    "?patient=Patient/123"
    "&code=http://loinc.org|8302-2"
    "&date=ge2023-01-01"
    "&date=le2023-12-31"
    "&status=final"
    "&_sort=-date"
    "&_count=20"
    "&_include=Observation:performer"
)

result = validator.validate_query(query)

if result.is_valid:
    print("All parameters valid")
else:
    for error in result.errors:
        print(f"ERROR: {error}")
Repeating the same parameter (like date=ge...&date=le...) is valid FHIR syntax for expressing a range. The validator treats each occurrence independently.

Common patterns that look correct but fail validation

Comparison prefixes are only valid on date, number, and quantity parameters. Applying one to a string parameter is a validation error:
# INVALID: 'name' is a string parameter; 'gt' prefix does not apply
result = validator.validate_query("Patient?name=gtSmith")
print(result.is_valid)  # False
print(result.errors)    # ["Comparison prefix 'gt' is not valid for string parameter 'name'"]
:exact and :contains are string modifiers. Applying them to token parameters like status or code fails:
# INVALID: 'status' is a token parameter, not a string
result = validator.validate_query("Observation?status:contains=fin")
print(result.is_valid)  # False
The source resource in _include must match the resource being searched, and the parameter must be a reference type defined for that resource:
# INVALID: 'subject' in _include must match the queried resource (Patient, not Observation)
result = validator.validate_query(
    "Patient?name=Smith&_include=Observation:subject"
)
print(result.is_valid)  # False
print(result.errors)    # ["_include source resource 'Observation' does not match queried resource 'Patient'"]
When specifying a system and code for a token parameter, the pipe character | separates them. A colon or slash is not valid:
# INVALID: colon used instead of pipe between system and code
result = validator.validate_query("Observation?code=http://loinc.org:8302-2")
print(result.is_valid)  # False

# VALID
result = validator.validate_query("Observation?code=http://loinc.org|8302-2")
print(result.is_valid)  # True
FHIR dates must be in ISO 8601 format (YYYY, YYYY-MM, or YYYY-MM-DD). Formats like MM/DD/YYYY or DD-MM-YYYY are invalid:
# INVALID: US date format
result = validator.validate_query("Patient?birthdate=01/15/1990")
print(result.is_valid)  # False
print(result.errors)    # ["Invalid date format '01/15/1990'. Use ISO 8601 (YYYY-MM-DD)."]

# VALID
result = validator.validate_query("Patient?birthdate=1990-01-15")
print(result.is_valid)  # True

Build docs developers (and LLMs) love