FHIR Server Guide¶
A comprehensive guide to the built-in FHIR R4 server with synthetic data generation.
What is the FHIR Server?¶
The fhir server command provides a fully functional FHIR R4 REST server designed for:
- Development & Testing: Quickly spin up a FHIR server with realistic synthetic data
- CQL Testing: Evaluate CQL expressions against generated patient data
- UI Development: Build and test FHIR-based applications without external dependencies
- CI/CD Pipelines: Reproducible test data for automated testing
- Demos & Training: Showcase FHIR applications with realistic data
Key Features¶
- Full FHIR REST API: CRUD operations, search, history, batch/transaction
- Synthetic Data Generation: Realistic clinical data using Faker and clinical code templates
- Preloading: Load CQL libraries, ValueSets, and existing FHIR data
- Reproducibility: Seed-based generation for consistent test data
- OpenAPI Documentation: Built-in Swagger UI for API exploration
Quick Start¶
Start Server with Synthetic Data¶
# Generate 100 patients with related clinical data
fhir serve --patients 100
# Output:
# Starting FHIR R4 Server
# Host: 0.0.0.0
# Port: 8080
# Synthetic patients: 100
#
# Endpoints:
# Web UI: http://0.0.0.0:8080/
# FHIR API: http://0.0.0.0:8080/baseR4
# Metadata: http://0.0.0.0:8080/baseR4/metadata
# API Docs: http://0.0.0.0:8080/docs
Make Your First Request¶
# List all patients
curl http://localhost:8080/baseR4/Patient
# Get a specific patient
curl http://localhost:8080/baseR4/Patient/patient-001
# Search by name
curl "http://localhost:8080/baseR4/Patient?name=Smith"
# Get capability statement
curl http://localhost:8080/baseR4/metadata
View Web UI¶
Open http://localhost:8080/ in your browser to browse resources through the web interface.
View API Documentation¶
Open http://localhost:8080/docs in your browser to see the interactive Swagger UI with all available endpoints.
CLI Reference¶
fhir serve¶
Start the FHIR server.
Options¶
| Option | Short | Default | Description |
|---|---|---|---|
--host |
-h |
0.0.0.0 |
Host to bind to |
--port |
-p |
8080 |
Port to bind to |
--patients |
-n |
0 |
Number of synthetic patients to generate |
--seed |
-s |
None | Random seed for reproducible data |
--preload-cql |
None | Directory of CQL files to preload | |
--preload-valuesets |
None | Directory of ValueSet/CodeSystem JSON files | |
--preload-data |
None | FHIR Bundle JSON file to preload | |
--reload |
-r |
False |
Enable auto-reload for development |
--log-level |
-l |
INFO |
Logging level |
Examples¶
# Start with 100 patients on custom port
fhir serve --patients 100 --port 9000
# Reproducible data with seed
fhir serve --patients 50 --seed 42
# Preload CQL libraries and ValueSets
fhir serve --preload-cql ./cql --preload-valuesets ./valuesets
# Load existing FHIR data
fhir serve --preload-data ./patients-bundle.json
# Development mode with auto-reload
fhir serve --patients 10 --reload
fhir server generate¶
Generate FHIR resources of a specific type. Supports all 37 resource types.
Arguments¶
| Argument | Description |
|---|---|
RESOURCE_TYPE |
Type of resource to generate (e.g., Patient, Observation) |
OUTPUT |
Output file path (optional - outputs to stdout if not specified) |
Options¶
| Option | Short | Default | Description |
|---|---|---|---|
--count |
-n |
1 |
Number of resources to generate |
--seed |
-s |
None | Random seed for reproducible data |
--format |
-f |
bundle |
Output format: bundle, ndjson, or json |
--pretty/--no-pretty |
True |
Pretty-print JSON output | |
--list |
-l |
List available resource types | |
--patient-ref |
None | Patient reference (e.g., Patient/123) | |
--practitioner-ref |
None | Practitioner reference | |
--organization-ref |
None | Organization reference | |
--encounter-ref |
None | Encounter reference | |
--hierarchy-depth |
None | For Location: generate hierarchy (1-6 levels) | |
--load |
False |
Load generated resources directly to FHIR server | |
--url |
-u |
http://localhost:8080 |
FHIR server URL (used with --load) |
Examples¶
# List all available resource types
fhir server generate --list
# Generate 5 patients to stdout
fhir server generate Patient -n 5
# Generate patients to file
fhir server generate Patient ./patients.json -n 10
# Generate observations linked to a patient
fhir server generate Observation -n 5 --patient-ref Patient/123
# Generate location hierarchy (Site → Building → Wing → Room)
fhir server generate Location --hierarchy-depth 4
# Generate with reproducible seed
fhir server generate Condition ./conditions.json -n 20 --seed 42
# Generate and load directly to server
fhir server generate Patient -n 10 --load --url http://localhost:8080
# Pipe to jq for processing
fhir server generate Patient -n 3 | jq '.entry[0].resource.name'
# Generate as NDJSON format
fhir server generate Patient -n 5 -f ndjson
fhir server populate¶
Populate a FHIR server with linked examples of all 37 resource types. Creates a complete, realistic dataset with proper references between resources.
Options¶
| Option | Short | Default | Description |
|---|---|---|---|
--url |
-u |
http://localhost:8080 |
FHIR server URL |
--patients |
-n |
3 |
Number of patients to generate |
--seed |
-s |
None | Random seed for reproducible data |
--dry-run |
False |
Generate but don't load to server | |
--output |
-o |
None | Save generated resources to file |
What Gets Generated¶
Resources are created in dependency order with proper linking:
- Foundation: Organizations, Location hierarchy (Site → Building → Wing → Room), Practitioners, ValueSet, CodeSystem
- Administrative: PractitionerRoles, Devices
- Scheduling: Schedules, Slots, Appointments
- Per patient:
- Patient, RelatedPerson
- Encounters with linked Conditions, Observations
- AllergyIntolerance, Immunization
- CareTeam, CarePlan, Goal, Task
- Medication, MedicationRequest, Procedure, ServiceRequest
- DiagnosticReport (linked to Observations), DocumentReference
- Coverage, Claim, ExplanationOfBenefit (financial chain)
- Quality Measures: Library → Measure → MeasureReport (per patient + summary)
- Groups: Patient cohort Group with member references
Examples¶
# Populate local server with 3 patients (default)
fhir server populate
# Populate with more patients
fhir server populate --patients 10
# Dry run - generate without loading
fhir server populate --dry-run --output ./population.json
# With reproducible seed
fhir server populate --seed 42 --patients 5
# Populate remote server
fhir server populate --url http://fhir.example.com --patients 20
fhir server load¶
Load FHIR resources from a file into a running server.
Options¶
| Option | Short | Default | Description |
|---|---|---|---|
--url |
-u |
http://localhost:8080 |
FHIR server base URL |
--batch/--no-batch |
True |
Use batch transaction for loading |
Examples¶
# Load a bundle into the running server
fhir server load ./data.json
# Load to a specific server
fhir server load ./data.json --url http://fhir.example.com
# Load resources individually (no batch)
fhir server load ./data.json --no-batch
fhir server stats¶
Show statistics for a running FHIR server.
Options¶
| Option | Short | Default | Description |
|---|---|---|---|
--url |
-u |
http://localhost:8080 |
FHIR server base URL |
Example¶
fhir server stats
# Output:
# FHIR Server Statistics
# URL: http://localhost:8080
#
# ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
# ┃ Resource Type ┃ Count ┃
# ┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
# │ Condition │ 342 │
# │ Encounter │ 650 │
# │ MedicationRequest │ 215 │
# │ Observation │ 1847 │
# │ Organization │ 100 │
# │ Patient │ 100 │
# │ Practitioner │ 100 │
# │ Procedure │ 156 │
# │ ──────────────── │ ───── │
# │ Total │ 3510 │
# └───────────────────┴───────┘
fhir server info¶
Show server capability statement information.
Example¶
fhir server info
# Output:
# FHIR Server Information
#
# Name: FHIR R4 Server
# Publisher: fhirkit
# FHIR Version: 4.0.1
# Status: active
#
# ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
# ┃ Type ┃ Interactions ┃ Search Params┃
# ┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
# │ Patient │ read, search, create │ 13 │
# │ Condition │ read, search, create │ 9 │
# │ Observation │ read, search, create │ 9 │
# │ ... │ ... │ ... │
# └───────────────────┴───────────────────────┴──────────────┘
REST API Reference¶
The FHIR server implements the FHIR R4 REST API specification.
Base URL¶
Content Types¶
All endpoints accept and return application/fhir+json.
Capability Statement¶
Returns the CapabilityStatement describing server capabilities.
CRUD Operations¶
Create Resource¶
Returns 201 Created with Location header.
curl -X POST http://localhost:8080/baseR4/Patient \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Patient",
"name": [{"family": "Smith", "given": ["John"]}],
"gender": "male"
}'
Read Resource¶
Returns the resource or 404 Not Found.
Update Resource¶
Updates existing resource or creates with specific ID.
curl -X PUT http://localhost:8080/baseR4/Patient/patient-001 \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Patient",
"id": "patient-001",
"name": [{"family": "Smith", "given": ["John", "William"]}],
"gender": "male"
}'
Delete Resource¶
Returns 204 No Content on success.
Search Operations¶
Common Search Parameters¶
Available for all resource types:
| Parameter | Type | Description |
|---|---|---|
_id |
token | Resource ID |
_count |
number | Results per page (default: 100, max: 1000) |
_offset |
number | Pagination offset |
_sort |
string | Sort field (prefix with - for descending) |
_total |
string | Control total count: accurate (default), estimate, none |
_contained |
string | Include contained resources: false (default), true, both |
_filter |
string | Advanced filter expression (see below) |
_filter Parameter¶
The _filter parameter enables complex boolean expressions beyond simple search:
# Find active male patients born after 1980
curl "http://localhost:8080/baseR4/Patient?_filter=gender%20eq%20male%20and%20active%20eq%20true%20and%20birthDate%20ge%201980-01-01"
# Find patients with name containing "son"
curl "http://localhost:8080/baseR4/Patient?_filter=family%20co%20son"
# Complex grouping
curl "http://localhost:8080/baseR4/Patient?_filter=(gender%20eq%20male%20or%20gender%20eq%20female)%20and%20active%20eq%20true"
Operators: eq, ne, gt, lt, ge, le, co (contains), sw (starts with), ew (ends with)
Logical: and, or, not, parentheses ()
See _filter Parameter for detailed documentation.
Search Parameter Types¶
| Type | Format | Example |
|---|---|---|
token |
[system|]code |
code=http://loinc.org|8480-6 |
string |
text (starts-with) | name=Smith |
reference |
[Type/]id |
patient=Patient/123 |
date |
[prefix]YYYY-MM-DD |
birthdate=ge1990-01-01 |
uri |
exact match | url=http://example.com/vs |
Date Prefixes¶
| Prefix | Meaning |
|---|---|
eq |
Equals (default) |
ne |
Not equals |
gt |
Greater than |
lt |
Less than |
ge |
Greater or equal |
le |
Less or equal |
Search Examples¶
# Find patients named Smith
curl "http://localhost:8080/baseR4/Patient?name=Smith"
# Find female patients
curl "http://localhost:8080/baseR4/Patient?gender=female"
# Find patients born after 1990
curl "http://localhost:8080/baseR4/Patient?birthdate=ge1990-01-01"
# Find conditions for a patient
curl "http://localhost:8080/baseR4/Condition?patient=Patient/patient-001"
# Find observations by LOINC code
curl "http://localhost:8080/baseR4/Observation?code=http://loinc.org|8480-6"
# Combined search with pagination
curl "http://localhost:8080/baseR4/Observation?patient=Patient/001&_count=50&_offset=100"
Resource-Specific Search Parameters¶
Patient¶
| Parameter | Type | Description |
|---|---|---|
identifier |
token | Patient identifier |
name |
string | Any part of name |
family |
string | Family name |
given |
string | Given name |
gender |
token | Gender code |
birthdate |
date | Birth date |
address |
string | Any part of address |
address-city |
string | City |
address-state |
string | State |
address-postalcode |
string | Postal code |
telecom |
token | Contact point value |
active |
token | Active status |
Condition¶
| Parameter | Type | Description |
|---|---|---|
patient |
reference | Patient reference |
subject |
reference | Subject reference |
code |
token | Condition code |
clinical-status |
token | Clinical status |
verification-status |
token | Verification status |
category |
token | Category |
onset-date |
date | Onset date |
severity |
token | Severity |
Observation¶
| Parameter | Type | Description |
|---|---|---|
patient |
reference | Patient reference |
subject |
reference | Subject reference |
code |
token | Observation code |
category |
token | Category |
status |
token | Status |
date |
date | Effective date |
value-quantity |
quantity | Value |
encounter |
reference | Encounter reference |
MedicationRequest¶
| Parameter | Type | Description |
|---|---|---|
patient |
reference | Patient reference |
code |
token | Medication code |
status |
token | Status |
intent |
token | Intent |
authoredon |
date | Authored date |
requester |
reference | Requester reference |
encounter |
reference | Encounter reference |
Encounter¶
| Parameter | Type | Description |
|---|---|---|
patient |
reference | Patient reference |
status |
token | Status |
class |
token | Encounter class |
type |
token | Encounter type |
date |
date | Period start |
participant |
reference | Participant reference |
service-provider |
reference | Service provider |
Procedure¶
| Parameter | Type | Description |
|---|---|---|
patient |
reference | Patient reference |
code |
token | Procedure code |
status |
token | Status |
date |
date | Performed date |
category |
token | Category |
performer |
reference | Performer reference |
encounter |
reference | Encounter reference |
History Operations¶
Instance History¶
Returns all versions of a resource.
Version Read¶
Returns a specific version.
Batch and Transaction Operations¶
The server supports both batch and transaction Bundle types with different processing semantics.
Batch Operations¶
POST /
Content-Type: application/fhir+json
{
"resourceType": "Bundle",
"type": "batch",
"entry": [...]
}
Process multiple operations independently. Failures in one entry do not affect others.
curl -X POST http://localhost:8080 \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Bundle",
"type": "batch",
"entry": [
{
"resource": {"resourceType": "Patient", "name": [{"family": "Test"}]},
"request": {"method": "POST", "url": "Patient"}
},
{
"request": {"method": "GET", "url": "Patient/patient-001"}
}
]
}'
Transaction Operations (Atomic)¶
POST /
Content-Type: application/fhir+json
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [...]
}
Process all operations atomically (all-or-nothing). If any entry fails, ALL changes are rolled back.
curl -X POST http://localhost:8080/baseR4 \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"resource": {"resourceType": "Patient", "id": "p1", "name": [{"family": "Smith"}]},
"request": {"method": "PUT", "url": "Patient/p1"}
},
{
"resource": {"resourceType": "Observation", "id": "o1", "status": "final", "code": {"text": "Test"}, "subject": {"reference": "Patient/p1"}},
"request": {"method": "PUT", "url": "Observation/o1"}
}
]
}'
| Feature | Batch | Transaction |
|---|---|---|
| Atomicity | No | Yes |
| Rollback on failure | No | Yes |
| Independent entries | Yes | No |
| Response type | batch-response | transaction-response |
| Use case | Bulk operations | Related changes |
See Transactions for detailed documentation on transaction atomicity.
Conditional Operations¶
FHIR conditional operations allow creating, updating, and deleting resources based on search criteria instead of resource IDs. Conditional read enables efficient caching.
Conditional Read (Caching)¶
Use conditional read headers to avoid transferring unchanged resources:
If-None-Match - Compare ETags (resource versions):
# First, get the resource and note the ETag
curl -i http://localhost:8080/baseR4/Patient/123
# Response headers include: ETag: W/"1"
# Later, check if resource changed
curl -i -H 'If-None-Match: W/"1"' http://localhost:8080/baseR4/Patient/123
# Returns 304 Not Modified if unchanged, 200 with body if changed
If-Modified-Since - Compare timestamps:
curl -i -H 'If-Modified-Since: Mon, 16 Dec 2024 00:00:00 GMT' \
http://localhost:8080/baseR4/Patient/123
# Returns 304 if not modified since that date
| Header | Value | Result |
|---|---|---|
| If-None-Match | W/"version" |
304 if ETag matches current version |
| If-None-Match | * |
304 if resource exists |
| If-Modified-Since | HTTP date | 304 if not modified since date |
| (none) | - | 200 with full resource |
Multiple ETags:
curl -i -H 'If-None-Match: W/"1", W/"2", W/"3"' \
http://localhost:8080/baseR4/Patient/123
# Returns 304 if current version matches any listed ETag
Conditional Create¶
Prevent duplicate resources using the If-None-Exist header:
POST /{ResourceType}
If-None-Exist: identifier=MRN123
Content-Type: application/fhir+json
{resource body}
| Result | Status | Description |
|---|---|---|
| No match | 201 | Resource created |
| 1 match | 200 | Existing resource returned (no change) |
| >1 match | 412 | Precondition Failed |
curl -X POST http://localhost:8080/baseR4/Patient \
-H "Content-Type: application/fhir+json" \
-H "If-None-Exist: identifier=http://hospital.org|MRN123" \
-d '{
"resourceType": "Patient",
"identifier": [{"system": "http://hospital.org", "value": "MRN123"}],
"name": [{"family": "Smith"}]
}'
Conditional Update¶
Update a resource by search criteria instead of ID:
| Result | Status | Description |
|---|---|---|
| No match | 201 | Resource created |
| 1 match | 200 | Resource updated |
| >1 match | 412 | Precondition Failed |
curl -X PUT "http://localhost:8080/baseR4/Patient?identifier=http://hospital.org|MRN123" \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Patient",
"identifier": [{"system": "http://hospital.org", "value": "MRN123"}],
"name": [{"family": "Smith", "given": ["John"]}]
}'
Conditional Delete¶
Delete resources matching search criteria:
Deletes all matching resources and returns 204 No Content.
# Delete all cancelled observations
curl -X DELETE "http://localhost:8080/baseR4/Observation?status=cancelled"
# Delete conditions for a specific patient
curl -X DELETE "http://localhost:8080/baseR4/Condition?patient=Patient/123"
Note: Conditional delete requires at least one search parameter to prevent accidental deletion of all resources.
Terminology Operations¶
ValueSet $expand¶
Expand a ValueSet to list all codes.
# By URL
curl "http://localhost:8080/baseR4/ValueSet/\$expand?url=http://example.com/vs/conditions"
# By ID
curl http://localhost:8080/baseR4/ValueSet/vs-001/\$expand
# With filter
curl "http://localhost:8080/baseR4/ValueSet/\$expand?url=http://example.com/vs&filter=diabetes"
CodeSystem $lookup¶
Look up information about a code.
ValueSet $validate-code¶
Validate that a code is in a ValueSet.
Bulk Data Export¶
The server implements the FHIR Bulk Data Access IG for asynchronous export of large datasets.
System Export¶
Export all resources from the server.
Patient Export¶
Export all patient data and related clinical resources.
Group Export¶
Export data for all patients in a Group.
Export Workflow¶
# 1. Start export (returns 202 with Content-Location header)
curl -X GET "http://localhost:8080/baseR4/\$export?_type=Patient,Observation" \
-H "Prefer: respond-async"
# 2. Poll status (returns 202 while in progress, 200 when complete)
curl http://localhost:8080/baseR4/bulk-status/{job-id}
# 3. Download NDJSON files from manifest
curl http://localhost:8080/baseR4/bulk-output/{job-id}/Patient.ndjson
# 4. Delete job when done
curl -X DELETE http://localhost:8080/baseR4/bulk-status/{job-id}
Parameters¶
| Parameter | Description |
|---|---|
_type |
Comma-separated resource types to export |
_since |
Only export resources updated after this datetime |
See Bulk Data Export for detailed documentation.
Synthetic Data Generation¶
Supported Resource Types¶
The server supports 37 FHIR R4 resource types organized by category:
Administrative Resources¶
| Resource Type | Description |
|---|---|
| Patient | Demographics, identifiers, contacts, emergency contacts |
| Practitioner | Healthcare providers with NPI, qualifications |
| PractitionerRole | Practitioner roles within organizations |
| Organization | Healthcare organizations with identifiers |
| Location | Physical locations with hierarchy support |
| RelatedPerson | Patient contacts and relationships |
Clinical Resources¶
| Resource Type | Description |
|---|---|
| Encounter | Patient visits (ambulatory, emergency, inpatient, virtual) |
| Condition | Medical diagnoses with clinical status |
| Observation | Vitals and lab results with reference ranges |
| Procedure | Medical procedures with codes |
| DiagnosticReport | Lab and imaging reports linked to Observations |
| AllergyIntolerance | Allergies with reactions and severity |
| Immunization | Vaccination records with codes |
Medication Resources¶
| Resource Type | Description |
|---|---|
| Medication | Medication definitions |
| MedicationRequest | Prescriptions with dosing instructions |
Care Management Resources¶
| Resource Type | Description |
|---|---|
| CarePlan | Care plans with activities and goals |
| CareTeam | Care team members and roles |
| Goal | Patient health goals |
| Task | Workflow tasks |
Scheduling Resources¶
| Resource Type | Description |
|---|---|
| Schedule | Provider schedules |
| Slot | Available appointment slots |
| Appointment | Booked appointments |
Financial Resources¶
| Resource Type | Description |
|---|---|
| Coverage | Insurance coverage information |
| Claim | Healthcare claims with line items |
| ExplanationOfBenefit | EOB with adjudication details |
Device Resources¶
| Resource Type | Description |
|---|---|
| Device | Medical devices with UDI |
Document Resources¶
| Resource Type | Description |
|---|---|
| ServiceRequest | Orders and referrals |
| DocumentReference | Clinical documents and attachments |
Forms & Consent Resources¶
| Resource Type | Description |
|---|---|
| Questionnaire | Form definitions (PHQ-9, GAD-7, intake forms) |
| QuestionnaireResponse | Completed form responses |
| Consent | Privacy and research consent records |
Quality Measure Resources¶
| Resource Type | Description |
|---|---|
| Library | CQL libraries with embedded content |
| Measure | Quality measure definitions |
| MeasureReport | Measure evaluation results |
Terminology Resources¶
| Resource Type | Description |
|---|---|
| ValueSet | Code value sets |
| CodeSystem | Code system definitions |
Group Resources¶
| Resource Type | Description |
|---|---|
| Group | Patient cohorts and populations |
Clinical Code Systems¶
SNOMED CT Conditions¶
The generator includes 50+ common conditions:
| Code | Display |
|---|---|
| 44054006 | Diabetes mellitus |
| 38341003 | Hypertensive disorder |
| 195967001 | Asthma |
| 55822004 | Hyperlipidemia |
| 414916001 | Obesity |
| 35489007 | Depression |
| 197480006 | Anxiety disorder |
| 13645005 | COPD |
| 396275006 | Osteoarthritis |
| ... | + 40 more |
LOINC Vital Signs¶
| Code | Display | Unit | Normal Range |
|---|---|---|---|
| 8310-5 | Body temperature | Cel | 36.1-37.2 |
| 8867-4 | Heart rate | /min | 60-100 |
| 8480-6 | Systolic blood pressure | mm[Hg] | 90-120 |
| 8462-4 | Diastolic blood pressure | mm[Hg] | 60-80 |
| 2708-6 | Oxygen saturation | % | 95-100 |
| 39156-5 | BMI | kg/m2 | 18.5-24.9 |
| 9279-1 | Respiratory rate | /min | 12-20 |
LOINC Laboratory Tests¶
| Code | Display | Unit | Normal Range |
|---|---|---|---|
| 2345-7 | Glucose | mg/dL | 70-100 |
| 4548-4 | HbA1c | % | 4.0-5.6 |
| 2160-0 | Creatinine | mg/dL | 0.7-1.3 |
| 33914-3 | eGFR | mL/min/1.73m2 | >60 |
| 2093-3 | Total cholesterol | mg/dL | <200 |
| 2089-1 | LDL cholesterol | mg/dL | <100 |
| 2085-9 | HDL cholesterol | mg/dL | >40 |
| 2571-8 | Triglycerides | mg/dL | <150 |
RxNorm Medications¶
The generator includes 100+ common medications:
- Diabetes: Metformin, insulin, glipizide
- Cardiovascular: Lisinopril, amlodipine, atorvastatin, metoprolol
- GI: Omeprazole, pantoprazole
- Pain: Ibuprofen, acetaminophen
- Respiratory: Albuterol, fluticasone
- And many more...
Data Realism¶
The generator creates realistic clinical scenarios:
- Weighted distributions: Common conditions appear more frequently
- Realistic vital ranges: Values within normal/abnormal ranges
- Temporal relationships: Encounters happen over time
- Status consistency: Active conditions, completed encounters
- Reference integrity: All references resolve correctly
Reproducibility¶
Use --seed for deterministic data generation:
# These commands produce identical data
fhir serve --patients 100 --seed 42
fhir server generate ./data.json --patients 100 --seed 42
This is useful for: - Automated testing with known data - Reproducing bugs - Consistent demos
Preloading Data¶
CQL Libraries¶
Preload CQL files as Library resources:
Directory structure:
Each CQL file is converted to a FHIR Library resource and can be retrieved:
ValueSets and CodeSystems¶
Preload terminology resources:
Supports: - Individual JSON files - FHIR Bundles containing multiple resources - Nested directories
Existing FHIR Data¶
Preload FHIR resources from a file:
Supports: - Single resources - FHIR Bundles
Python API¶
FHIRStore Class¶
from fhirkit.server.storage import FHIRStore
# Create store
store = FHIRStore()
# CRUD operations
patient = {
"resourceType": "Patient",
"name": [{"family": "Smith", "given": ["John"]}]
}
# Create - assigns ID and meta
created = store.create(patient)
print(created["id"]) # e.g., "abc123"
print(created["meta"]["versionId"]) # "1"
# Read
patient = store.read("Patient", "abc123")
# Update - increments version
patient["name"][0]["given"].append("William")
updated = store.update("Patient", "abc123", patient)
print(updated["meta"]["versionId"]) # "2"
# Delete
deleted = store.delete("Patient", "abc123")
# Search
resources, total = store.search(
resource_type="Patient",
params={"name": "Smith"},
_count=50,
_offset=0
)
# History
versions = store.history("Patient", "abc123")
PatientRecordGenerator¶
from fhirkit.server.generator import PatientRecordGenerator
# Create generator with seed for reproducibility
generator = PatientRecordGenerator(seed=42)
# Generate a single patient record with all related resources
resources = generator.generate_patient_record(
num_conditions=(2, 6), # 2-6 conditions per patient
num_encounters=(3, 10), # 3-10 encounters
num_observations_per_encounter=(2, 5),
num_medications=(1, 5),
num_procedures=(0, 3)
)
print(f"Generated {len(resources)} resources")
# Generated 45 resources (Patient, Practitioner, Organization,
# Encounters, Conditions, Observations, etc.)
# Generate a population
all_resources = generator.generate_population(num_patients=100)
print(f"Total: {len(all_resources)} resources")
Individual Resource Generators¶
from fhirkit.server.generator import (
PatientGenerator,
ConditionGenerator,
ObservationGenerator,
MedicationRequestGenerator,
PractitionerGenerator,
OrganizationGenerator,
EncounterGenerator,
ProcedureGenerator,
)
# Create generators
patient_gen = PatientGenerator(seed=42)
condition_gen = ConditionGenerator(seed=42)
# Generate resources
patient = patient_gen.generate()
patient_ref = f"Patient/{patient['id']}"
condition = condition_gen.generate(patient_ref=patient_ref)
print(condition["code"]["coding"][0]["display"])
# e.g., "Diabetes mellitus"
Creating Custom Server¶
from fhirkit.server.api import create_app
from fhirkit.server.config import FHIRServerSettings
from fhirkit.server.storage import FHIRStore
# Custom settings
settings = FHIRServerSettings(
host="0.0.0.0",
port=8080,
patients=50,
seed=42,
enable_docs=True,
enable_cors=True,
cors_origins=["http://localhost:3000"],
)
# Create store and pre-populate
store = FHIRStore()
# Create FastAPI app
app = create_app(settings=settings, store=store)
# Run with uvicorn
import uvicorn
uvicorn.run(app, host=settings.host, port=settings.port)
Configuration¶
Environment Variables¶
All settings can be configured via environment variables with the FHIR_SERVER_ prefix:
| Variable | Default | Description |
|---|---|---|
FHIR_SERVER_HOST |
0.0.0.0 |
Host to bind to |
FHIR_SERVER_PORT |
8080 |
Port to bind to |
FHIR_SERVER_PATIENTS |
0 |
Synthetic patients |
FHIR_SERVER_SEED |
None | Random seed |
FHIR_SERVER_PRELOAD_CQL |
None | CQL directory |
FHIR_SERVER_PRELOAD_VALUESETS |
None | Terminology directory |
FHIR_SERVER_ENABLE_CORS |
True |
Enable CORS |
FHIR_SERVER_CORS_ORIGINS |
["*"] |
CORS allowed origins |
FHIR_SERVER_ENABLE_DOCS |
True |
Enable Swagger UI |
FHIR_SERVER_LOG_LEVEL |
INFO |
Logging level |
FHIR_SERVER_DEFAULT_PAGE_SIZE |
100 |
Default search results |
FHIR_SERVER_MAX_PAGE_SIZE |
1000 |
Maximum search results |
Using .env File¶
Create a .env file:
Then start the server:
FHIRServerSettings Class¶
from fhirkit.server.config import FHIRServerSettings
settings = FHIRServerSettings(
# Server
server_name="My FHIR Server",
host="0.0.0.0",
port=8080,
base_path="",
# Data generation
patients=100,
seed=42,
# Preloading
preload_cql="/path/to/cql",
preload_valuesets="/path/to/valuesets",
preload_data="/path/to/bundle.json",
# Features
enable_terminology=True,
enable_docs=True,
enable_cors=True,
cors_origins=["*"],
# Logging
log_level="INFO",
log_requests=True,
# Limits
default_page_size=100,
max_page_size=1000,
)
Examples¶
Testing CQL Against Synthetic Data¶
# Start server with synthetic data
fhir serve --patients 100 --seed 42 &
# Run CQL evaluation against the server
fhir cql eval "
define DiabetesPatients:
[Condition: Code 'http://snomed.info/sct|44054006']
" --data http://localhost:8080
Integration with CDS Hooks¶
# Start FHIR server with data
fhir serve --patients 50 --port 8080 &
# Start CDS Hooks server pointing to FHIR server
fhir cds serve --config ./cds-config.yaml --fhir-url http://localhost:8080
Generating Test Data for CI/CD¶
# Generate reproducible test data
fhir server generate ./test-data.json --patients 20 --seed 12345
# Use in tests
pytest --fhir-data=./test-data.json
Docker Deployment¶
FROM python:3.13-slim
WORKDIR /app
COPY . .
RUN pip install fhir-cql
EXPOSE 8080
CMD ["fhir", "server", "serve", "--patients", "100"]
Troubleshooting¶
Common Issues¶
Server won't start¶
No data generated¶
# Ensure --patients is specified
fhir serve --patients 100 # Generates data
fhir serve # Empty server
Search returns no results¶
# Check server statistics
fhir server stats
# Verify search parameter format
# Correct:
curl "http://localhost:8080/baseR4/Patient?name=Smith"
# Incorrect (missing quote escaping):
curl http://localhost:8080/baseR4/Patient?name=Smith # May fail in some shells
Performance Considerations¶
- Memory: Each patient generates ~35 resources. 1000 patients ≈ 35,000 resources in memory
- Startup time: Large patient counts (1000+) may take a few seconds to generate
- Search: In-memory search scales linearly with resource count
For large datasets, consider:
- Using --preload-data with pre-generated data
- Generating data to files and loading on demand
- Using pagination (_count, _offset) for large result sets
API Documentation¶
The server provides built-in API documentation:
- Swagger UI: http://localhost:8080/docs
- ReDoc: http://localhost:8080/redoc
- OpenAPI JSON: http://localhost:8080/openapi.json
To disable documentation: