External FHIR servers as a data backend
Overview
Formbox module uses Aidbox's FHIR API capabilities to store all it's data. But sometimes it's appropriate to use other FHIR backends. Formbox provides a simple and granular way to specify external FHIR servers for data management.
Data Domains
Data used by Formbox has different nature and can have different storage requirements and strategies.
We split data in 4 domains, and last 3 of them can have it's own FHIR backend:
Because
system resourcesare non FHIR resources - there is no FHIR backend configuration for them.
System resources- non-FHIR resources, used byFormboxfor extended functionality.Content resources-Questionnaireresource (the main focus and result ofForms Builder).Terminology resources-ValueSetsandConcepts.Production resources-QuestionnaireResponseresource and other FHIR resources that contain PHI/Production data.
Storage Defaults
By default all data is stored in Aidbox and there is no need for any configuration.
Package storage requirements
Form packages support the same storage configuration as individual forms, in both standalone Formbox and the Aidbox module:
form-storehandles the package'sQuestionnaireresources.data-storehandlesPlanDefinition,RequestGroup,Task,QuestionnaireResponse, and extraction results. This includes the package definition, even though it is reusable content.
Package links, draft saves, conditions, prefills, submission, and amendments all use these stores. The external data store must support FHIR transaction bundles. Package submission sends status updates, responses, and extraction results in one bundle, using the same proxy mechanism as ordinary form extraction.
Each bundle is committed or rolled back by its data store, for both local and external storage. Separate writes within an operation are not covered by a shared transaction, and Formbox does not coordinate rollback across servers. See the package submission API for failure handling.
Storage Configuration
Storage configuration lives in default SDCConfig resource. (read more)
IMPORTANT: config should have default: true property set
Storage Configuration structure
For every domain we have separate key:
form-store- content resources domaindata-store- production resources domainterm-server- terminology resources domain
One more key names a service rather than a storage domain:
dtr-service- external Da Vinci DTR service, see DTR service
To enable external FHIR Backend we should specify next fields for it:
endpoint- URI to FHIR APIheaders(optional) - Additional HTTP headers for requests (Authorization for example)auth(optional) - OAuth2client_credentialscredentials, see Authentication
Example:
[store-key]:
endpoint: 'http://my-ehr.com/fhir'
headers:
Authorization: 'Basic cm9vdDpzZWNyZXQ='
Authentication
A server that accepts a fixed credential takes it from headers, as in the example above.
A server that issues tokens is described with auth instead. Formbox requests an access token with the OAuth2 client_credentials grant on the first call and reuses it until it expires, then sends it as Authorization: Bearer <token>. A bearer obtained this way replaces an Authorization header set in headers.
token-endpoint- OAuth2 token endpoint that issues the access tokenclient-id- OAuth2 client idclient-secret- OAuth2 client secretscope(optional) - scope requested with the token
data-store:
endpoint: 'https://my-ehr.com/fhir'
auth:
token-endpoint: 'https://my-ehr.com/oauth2/token'
client-id: 'formbox'
client-secret: 'ILUcZS3T...'
scope: 'system/*.read'
Where the client secret lives
Formbox takes the secret from the first source that has it:
-
A secret in the vault - the recommended option: the value stays in a mounted file and never reaches the database. Point the field at a secret from the vault configuration (
BOX_VAULT_CONFIG) and scope that secret toSDCConfig:auth: token-endpoint: 'https://my-ehr.com/oauth2/token' client-id: 'formbox' _client-secret: extension: - url: 'http://hl7.org/fhir/StructureDefinition/data-absent-reason' valueCode: masked - url: 'http://health-samurai.io/fhir/secret-reference' valueString: my-ehr-client-secret -
The value written into
client-secret- Aidbox encrypts it on write and stores the ciphertext, so the box needsBOX_SECURITY_ENCRYPT_SECRET. Without that setting the resource cannot be saved. -
Box settings -
BOX_SDC_DTR_CLIENT_IDandBOX_SDC_DTR_CLIENT_SECRETare used when the configuration carries no credentials of its own.
If the token endpoint cannot be reached, or no credentials are found, the call answers with an OperationOutcome naming the token endpoint.
Form Storage
Store Questionnaire resource only
WARN: assembled forms and components are not yet supported with external storage.
form-store:
endpoint: "https://fhir-server.com/fhir"
Data Storage
Store QuestionnaireResponse and all FHIR Resources except Questionnaire.
This store will be used as source in $populate operations, and as target in $extract+$process-response operations
data-store:
endpoint: "https://fhir-server.com/fhir"
Terminology service
To learn how to configure a custom terminology server and add terminology servers to the configuration, see Integration with external terminology servers.
DTR service
dtr-service is not a storage domain: it names the service that answers the Da Vinci DTR operations $questionnaire-package and $next-question instead of Formbox. Payers such as MCG and InterQual own the form library behind those operations, so with it set Formbox resolves nothing locally and forwards the whole call.
dtr-service:
endpoint: 'https://dtr.payer.example'
auth:
token-endpoint: 'https://dtr.payer.example/oauth2/token'
client-id: 'formbox'
client-secret: 'ILUcZS3T...'
It takes the same endpoint, headers and auth fields as the stores above. See Da Vinci DTR for what is forwarded, how a single form can name a service of its own, and how each tenant reaches its own service.
Full config example:
POST /SDCConfig
content-type: text/yaml
id: cfg-1
resourceType: SDCConfig
name: Config with external fhir servers
default: true
form-store:
endpoint: 'http://forms-repo.com'
data-store:
endpoint: 'http://my-ehr.com/fhir'
headers:
Authorization: 'Basic cm9vdDpzZWNyZXQ='
term-server:
endpoint: 'http://tx.com/fhir'
dtr-service:
endpoint: 'https://dtr.payer.example'
auth:
token-endpoint: 'https://dtr.payer.example/oauth2/token'
client-id: 'formbox'
client-secret: 'ILUcZS3T...'
Response of an external server
The status and the body a server answers with reach the caller as they are: a 400 with an OperationOutcome from the external server arrives at the client as a 400 with that outcome. When the server cannot be reached at all, Formbox answers with an OperationOutcome that names the endpoint.
The server's own response headers and cookies stay at the proxy — a gateway in front of the external server does not leak its session into the Formbox response.
Multitenancy Support
- Default system
SDCCOnfig(forrootOrganization) does not take effect on tenants - Every tenant have their own default config and thus separate FHIR backends setup