Skip to content

FHIR Interoperability

quickq uses HL7 FHIR R4 as its cross-language handoff protocol. The OLTP schema is a superset of FHIR Questionnaire — every questionnaire authored in quickq can be exported to valid FHIR JSON without loss, and any valid FHIR Questionnaire can be imported.


The Handoff Model

Survey delivery is out of scope for quickq. The FHIR exchange is the interface:

quickq  →  export_fhir()  →  FHIR Questionnaire JSON
                                  ↓
                          any FHIR-compliant delivery tool
                          (LHC-Forms, REDCap, mobile app, clinical system)
                                  ↓
                          FHIR QuestionnaireResponse JSON
                                  ↓
quickq  →  import_fhir_response()  →  OLTP response rows  →  quickq refresh

This means quickq Python is not required at delivery time. A JavaScript app, a WASM binary, or a third-party clinical platform can deliver the questionnaire using only the exported JSON. quickq re-enters the picture when the responses come back.

The reference delivery tool is LHC-Forms (NLM) — purpose-built for FHIR Questionnaire rendering, open source, embeddable as a JavaScript widget with no server dependency.


Column Mapping

The OLTP schema maps directly to FHIR fields. Fields that have no FHIR equivalent are serialized as extensions on export.

OLTP column FHIR field
questionnaire.canonical_url Questionnaire.url
questionnaire.version Questionnaire.version
questionnaire.fhir_status Questionnaire.status
question.link_id item.linkId
question.question_text item.text
question.help_text item._text / extension
question.question_type item.type (see table below)
questionnaire_question.is_required item.required
response_option.option_text answerOption.valueCoding.display
response_option.option_value answerOption.valueCoding.code
response_option.concept_code + .concept_system valueCoding.code + system
response_option_set.canonical_url item.answerValueSet
skip_rule rows item.enableWhen[]
questionnaire_question.parent_qq_id nested item arrays
response_session QuestionnaireResponse header
response rows QuestionnaireResponse.item[].answer[]
response.repeat_index repeated item entries with the same linkId

Question type mapping

This table maps each quickq question type to its FHIR item.type. For the canonical list (with YAML authoring syntax and current pipeline-coverage status per type), see the Question Type Reference.

quickq type FHIR type Notes
single_choice choice
multiple_choice choice repeats: true
boolean boolean
text text
numeric decimal
date date
datetime dateTime
likert choice
grid group rows as nested items
ranked choice ordinalValue extensions
slider decimal min/max extensions
repeating_group group repeats: true; children nested in item

Extensions

Research fields with no FHIR R4 base equivalent are serialized as extensions under https://quickq.io/fhir/StructureDefinition/:

Extension Source column
help-text question.help_text
internal-note question.internal_note
source-instrument question.source_instrument
source-item-id question.source_item_id
scoring-rule scoring_rule rows
count-question questionnaire_question.count_qq_id (repeating group count link)

Import

import_fhir(conn, fhir_json) imports a FHIR Questionnaire into the OLTP. Non-repeating group items are flattened to their leaf questions. Repeating groups (type: group, repeats: true) are imported as repeating_group questions with their children linked via parent_qq_id.

import_fhir_response(conn, resource) parses a QuestionnaireResponse and writes response rows. The questionnaire is matched by canonical_url. Repeated group instances — multiple item entries with the same linkId — are assigned sequential repeat_index values (0-based). Unknown linkId values and unrecognized answer formats are written to data_quality_flag rather than raising exceptions.

Supported nested-item shapes

Shape Supported Notes
Top-level group (non-repeating) Children flatten to leaf questions during Questionnaire import; responses target the leaves directly.
Top-level group, repeats: true (repeating group) Each item entry produces a repeat_index increment. Children populate response.repeat_index per instance.
Repeating group with simple-type children (boolean, numeric, date, text, single_choice, multiple_choice, sata_other, likert, ranked, slider) All answer-bearing children inherit the parent's repeat_index.
Repeating group with a grid child Grid cells inherit the containing instance's repeat_index. Covered by tests/test_repeating_nested_boundary.py.
Repeating group with a non-repeating group child Not modelled — non-repeating groups don't exist as a question_type in quickq; they flatten at Questionnaire-import time.
Repeating group with a repeating_group child (nested loops) Logged to data_quality_flag with rule_name = nested_repeating_group and skipped. The schema doesn't model the index-pair; see quickq-io-ap8 for the structural roadmap.

Round-Trip Guarantee

Any FHIR Questionnaire that can be imported can be exported back to valid FHIR without loss of structure or semantics. The round-trip is tested against the HL7 reference example suite, including the US Surgeon General Family Health Portrait (USSG-FHT, LOINC 54127-6) which exercises nested repeating groups at two levels.

quickq fhir import ussg_fht.json study.db
quickq fhir export study.db 1 > ussg_exported.json

The FHIR E2E test suite (in tests/test_e2e_lhcforms.py) validates the full pipeline against LHC-Forms using Playwright headless rendering.