# fhirstarter
<p>
<a href="https://github.com/canvas-medical/fhirstarter/actions?query=workflow%3Atests+event%3Apush+branch%3Amain" target="_blank">
<img src="https://github.com/canvas-medical/fhirstarter/workflows/tests/badge.svg?event=push&branch=main" alt="tests">
</a>
<a href="https://pypi.org/project/fhirstarter/">
<img src="https://img.shields.io/pypi/v/fhirstarter">
</a>
<a href="https://pypi.org/project/fhirstarter/">
<img src="https://img.shields.io/pypi/pyversions/fhirstarter">
</a>
<a href="https://pypi.org/project/fhirstarter/">
<img src="https://img.shields.io/pypi/l/fhirstarter">
</a>
<a href="https://github.com/psf/black">
<img src="https://img.shields.io/badge/code%20style-black-000000">
</a>
</p>
An ASGI [FHIR](https://hl7.org/fhir/) API framework built on top of [FastAPI](https://fastapi.tiangolo.com) and
[FHIR Resources](https://pypi.org/project/fhir.resources/).
Supports FHIR sequences:
* [STU (v3.0.2)](https://hl7.org/fhir/STU3/)
* [R4 (v4.0.1)](https://hl7.org/fhir/R4/)
* [R4B (v4.3.0)](https://hl7.org/fhir/R4B/)
* [R5 (v5.0.0)](https://hl7.org/fhir/R5/)
## Installation
```bash
pip install fhirstarter
```
## Features
* Automatic, standardized API route creation
* Automatic validation of inputs and outputs through the use of FHIR Resources Pydantic models
* Automatically-generated capability statement that can be customized, and a capability statement
API route
* An exception-handling framework that produces FHIR-friendly responses (i.e. OperationOutcomes)
* Automatically-generated, integrated documentation generated from the FHIR specification
* Custom search parameters for search endpoints
### Disclaimer
FHIRStarter was built based on the business needs of
[Canvas Medical](https://www.canvasmedical.com). At any point in time, it may not be broadly
applicable to the industry at large. Canvas Medical open-sourced the project so that it can be used
by healthcare software developers whose needs it might also meet. Ongoing support and development
will be based on the business needs of Canvas Medical.
## Background
FHIRStarter uses a provider-decorator pattern. Developers can write functions, or handlers, that
implement FHIR interactions and plug them into the framework. FHIRStarter then automatically creates
FHIR-compatible API routes from these developer-provided functions. Handlers that are supplied must
use the resource classes defined by the [FHIR Resources](https://pypi.org/project/fhir.resources/) Python package, which is a collection
of Pydantic models for FHIR resources.
In order to stand up a FHIR server, all that is required is to create a FHIRStarter and a
FHIRProvider instance, register a FHIR interaction with the provider, add the provider to the
FHIRStarter instance, and pass the FHIRStarter instance to an ASGI server. An example is provided
below.
## Usage
### Currently-supported functionality
FHIRStarter will automatically generate the capability statement endpoint at `/metadata`. In
addition, it supports the following instance- and type-level interactions:
* [read](https://hl7.org/fhir/http.html#read)
* [update](https://hl7.org/fhir/http.html#update)
* [patch](https://hl7.org/fhir/http.html#patch)
* [delete](https://hl7.org/fhir/http.html#delete)
* [create](https://hl7.org/fhir/http.html#create)
* [search-type](https://hl7.org/fhir/http.html#search)
Interaction handlers can be written as coroutines with `async/await` syntax, or as plain functions.
FastAPI supports both, as does FHIRStarter.
Using uvloop can improve performance of the underlying event loop on supported platforms.
FHIRStarter does not mandate the use of uvloop, but it may be enabled by importing uvloop and
adding a snippet like this to your application startup script:
```python
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
```
### Configuration for specific FHIR sequences
FHIRStarter will work out of the box as an R5 server. If a different sequence is desired, it must be
specified with an environment variable:
```shell
FHIR_SEQUENCE=R4B
```
The latest version of the [FHIR Resources](https://pypi.org/project/fhir.resources/) package only
supports FHIR STU3, R4B, and R5. FHIR R4 is supported by an earlier version. Because of this, if a
developer desires to use FHIR R4, then the developer must pin version **6.4.0** of fhir.resources in
their project. FHIRStarter will check the version of fhir.resources against the specified FHIR
version in the environment variable to ensure that they are compatible.
Model imports are also affected by which version of fhir.resources is installed. For STU3 and R4B,
model imports will look like this:
```python
from fhir.resources.STU3.patient import Patient
```
```python
from fhir.resources.R4B.patient import Patient
```
For R4 and R5, model imports will look like this:
```python
from fhir.resources.patient import Patient
```
### Example
A detailed example is available here: [example.py](https://github.com/canvas-medical/fhirstarter/blob/main/fhirstarter/examples/example.py).
```python
import uvicorn
from fhir.resources.fhirtypes import Id
from fhir.resources.patient import Patient
from fhirstarter import FHIRProvider, FHIRStarter, InteractionContext
from fhirstarter.exceptions import FHIRResourceNotFoundError
# Create the app
app = FHIRStarter()
# Create a provider
provider = FHIRProvider()
# Register the patient read FHIR interaction with the provider
@provider.read(Patient)
async def patient_read(context: InteractionContext, id_: Id) -> Patient:
# Get the patient from the database
patient = ...
if not patient:
raise FHIRResourceNotFoundError
return Patient(
**{
# Map patient from database to FHIR Patient structure
}
)
# Add the provider to the app
app.add_providers(provider)
if __name__ == "__main__":
# Start the server
uvicorn.run(app)
```
### Custom search parameters
Custom search parameters can be defined in a configuration file that can be passed to the app on
creation.
```toml
[search-parameters.Patient.nickname]
type = "string"
description = "Nickname"
uri = "https://hostname/nickname"
include-in-capability-statement = true
```
Adding a custom search parameter via configuration allows this name to be used as an argument when
defining a search-type interaction handler and also adds this search parameter to the API
documentation for the search endpoint.
### Capability statement
It is possible to customize the capability statement by setting a capability statement modifier:
```python
def amend_capability_statement(
capability_statement: MutableMapping[str, Any], request: Request, response: Response
) -> MutableMapping[str, Any]:
capability_statement["publisher"] = "Canvas Medical"
return capability_statement
app.set_capability_statement_modifier(amend_capability_statement)
```
### FastAPI dependency injection
FastAPI's [dependency injection system](https://fastapi.tiangolo.com/tutorial/dependencies/) is exposed at various levels:
* **application**: the `__init__` method on the FHIRStarter class
* **provider**: the `__init__` method on the FHIRProvider class
* **handler**: the `read`, `update`, `create`, or `search_type` decorator used to add a handler to a provider
Dependencies specified at the application level will be injected into all routes in the application.
Dependencies specified at the provider level will be injected into all routes that are added to
the application from that specific provider.
Dependencies specified at the handler level only apply to that specific FHIR interaction.
Raw data
{
"_id": null,
"home_page": "https://github.com/canvas-medical/fhirstarter",
"name": "fhirstarter",
"maintainer": "Canvas Medical Engineering",
"docs_url": null,
"requires_python": "<3.13.0,>=3.8.0",
"maintainer_email": "engineering@canvasmedical.com",
"keywords": "fhir, api, server, resources, framework, fastapi, healthcare, hl7",
"author": "Christopher Sande",
"author_email": "christopher.sande@canvasmedical.com",
"download_url": "https://files.pythonhosted.org/packages/fb/66/b6949a777510e1bb0c54a413d70a9753d9db6f9be0a01eb47fa1b029faaf/fhirstarter-2.4.2.tar.gz",
"platform": null,
"description": "# fhirstarter\n\n<p>\n <a href=\"https://github.com/canvas-medical/fhirstarter/actions?query=workflow%3Atests+event%3Apush+branch%3Amain\" target=\"_blank\">\n <img src=\"https://github.com/canvas-medical/fhirstarter/workflows/tests/badge.svg?event=push&branch=main\" alt=\"tests\">\n </a>\n <a href=\"https://pypi.org/project/fhirstarter/\">\n <img src=\"https://img.shields.io/pypi/v/fhirstarter\">\n </a>\n <a href=\"https://pypi.org/project/fhirstarter/\">\n <img src=\"https://img.shields.io/pypi/pyversions/fhirstarter\">\n </a>\n <a href=\"https://pypi.org/project/fhirstarter/\">\n <img src=\"https://img.shields.io/pypi/l/fhirstarter\">\n </a>\n <a href=\"https://github.com/psf/black\">\n <img src=\"https://img.shields.io/badge/code%20style-black-000000\">\n </a>\n</p>\n\nAn ASGI [FHIR](https://hl7.org/fhir/) API framework built on top of [FastAPI](https://fastapi.tiangolo.com) and\n[FHIR Resources](https://pypi.org/project/fhir.resources/).\n\nSupports FHIR sequences:\n* [STU (v3.0.2)](https://hl7.org/fhir/STU3/)\n* [R4 (v4.0.1)](https://hl7.org/fhir/R4/)\n* [R4B (v4.3.0)](https://hl7.org/fhir/R4B/)\n* [R5 (v5.0.0)](https://hl7.org/fhir/R5/)\n\n## Installation\n\n```bash\npip install fhirstarter\n```\n\n## Features\n\n* Automatic, standardized API route creation\n* Automatic validation of inputs and outputs through the use of FHIR Resources Pydantic models\n* Automatically-generated capability statement that can be customized, and a capability statement\n API route\n* An exception-handling framework that produces FHIR-friendly responses (i.e. OperationOutcomes)\n* Automatically-generated, integrated documentation generated from the FHIR specification\n* Custom search parameters for search endpoints\n\n### Disclaimer\n\nFHIRStarter was built based on the business needs of\n[Canvas Medical](https://www.canvasmedical.com). At any point in time, it may not be broadly\napplicable to the industry at large. Canvas Medical open-sourced the project so that it can be used\nby healthcare software developers whose needs it might also meet. Ongoing support and development\nwill be based on the business needs of Canvas Medical.\n\n## Background\n\nFHIRStarter uses a provider-decorator pattern. Developers can write functions, or handlers, that\nimplement FHIR interactions and plug them into the framework. FHIRStarter then automatically creates\nFHIR-compatible API routes from these developer-provided functions. Handlers that are supplied must\n use the resource classes defined by the [FHIR Resources](https://pypi.org/project/fhir.resources/) Python package, which is a collection\nof Pydantic models for FHIR resources.\n\nIn order to stand up a FHIR server, all that is required is to create a FHIRStarter and a\nFHIRProvider instance, register a FHIR interaction with the provider, add the provider to the\nFHIRStarter instance, and pass the FHIRStarter instance to an ASGI server. An example is provided\nbelow.\n\n## Usage\n\n### Currently-supported functionality\n\nFHIRStarter will automatically generate the capability statement endpoint at `/metadata`. In\naddition, it supports the following instance- and type-level interactions:\n* [read](https://hl7.org/fhir/http.html#read)\n* [update](https://hl7.org/fhir/http.html#update)\n* [patch](https://hl7.org/fhir/http.html#patch)\n* [delete](https://hl7.org/fhir/http.html#delete)\n* [create](https://hl7.org/fhir/http.html#create)\n* [search-type](https://hl7.org/fhir/http.html#search)\n\nInteraction handlers can be written as coroutines with `async/await` syntax, or as plain functions.\nFastAPI supports both, as does FHIRStarter.\n\nUsing uvloop can improve performance of the underlying event loop on supported platforms. \nFHIRStarter does not mandate the use of uvloop, but it may be enabled by importing uvloop and\nadding a snippet like this to your application startup script:\n\n```python\nasyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n```\n\n### Configuration for specific FHIR sequences\n\nFHIRStarter will work out of the box as an R5 server. If a different sequence is desired, it must be\nspecified with an environment variable:\n\n```shell\nFHIR_SEQUENCE=R4B\n```\n\nThe latest version of the [FHIR Resources](https://pypi.org/project/fhir.resources/) package only\nsupports FHIR STU3, R4B, and R5. FHIR R4 is supported by an earlier version. Because of this, if a\ndeveloper desires to use FHIR R4, then the developer must pin version **6.4.0** of fhir.resources in\ntheir project. FHIRStarter will check the version of fhir.resources against the specified FHIR\nversion in the environment variable to ensure that they are compatible.\n\nModel imports are also affected by which version of fhir.resources is installed. For STU3 and R4B,\nmodel imports will look like this:\n\n```python\nfrom fhir.resources.STU3.patient import Patient\n```\n```python\nfrom fhir.resources.R4B.patient import Patient\n```\n\nFor R4 and R5, model imports will look like this:\n\n```python\nfrom fhir.resources.patient import Patient\n```\n\n### Example\n\nA detailed example is available here: [example.py](https://github.com/canvas-medical/fhirstarter/blob/main/fhirstarter/examples/example.py).\n\n```python\nimport uvicorn\nfrom fhir.resources.fhirtypes import Id\nfrom fhir.resources.patient import Patient\n\nfrom fhirstarter import FHIRProvider, FHIRStarter, InteractionContext\nfrom fhirstarter.exceptions import FHIRResourceNotFoundError\n\n# Create the app\napp = FHIRStarter()\n\n# Create a provider\nprovider = FHIRProvider()\n\n\n# Register the patient read FHIR interaction with the provider\n@provider.read(Patient)\nasync def patient_read(context: InteractionContext, id_: Id) -> Patient:\n # Get the patient from the database\n patient = ...\n\n if not patient:\n raise FHIRResourceNotFoundError\n\n return Patient(\n **{\n # Map patient from database to FHIR Patient structure\n }\n )\n\n\n# Add the provider to the app\napp.add_providers(provider)\n\n\nif __name__ == \"__main__\":\n # Start the server\n uvicorn.run(app)\n```\n\n### Custom search parameters\n\nCustom search parameters can be defined in a configuration file that can be passed to the app on\ncreation.\n\n```toml\n[search-parameters.Patient.nickname]\ntype = \"string\"\ndescription = \"Nickname\"\nuri = \"https://hostname/nickname\"\ninclude-in-capability-statement = true\n```\n\nAdding a custom search parameter via configuration allows this name to be used as an argument when\ndefining a search-type interaction handler and also adds this search parameter to the API\ndocumentation for the search endpoint.\n\n### Capability statement\n\nIt is possible to customize the capability statement by setting a capability statement modifier:\n\n```python\ndef amend_capability_statement(\n capability_statement: MutableMapping[str, Any], request: Request, response: Response\n) -> MutableMapping[str, Any]:\n capability_statement[\"publisher\"] = \"Canvas Medical\"\n return capability_statement\n\napp.set_capability_statement_modifier(amend_capability_statement)\n```\n\n### FastAPI dependency injection\n\nFastAPI's [dependency injection system](https://fastapi.tiangolo.com/tutorial/dependencies/) is exposed at various levels:\n\n* **application**: the `__init__` method on the FHIRStarter class\n* **provider**: the `__init__` method on the FHIRProvider class\n* **handler**: the `read`, `update`, `create`, or `search_type` decorator used to add a handler to a provider\n\nDependencies specified at the application level will be injected into all routes in the application.\n\nDependencies specified at the provider level will be injected into all routes that are added to\nthe application from that specific provider.\n\nDependencies specified at the handler level only apply to that specific FHIR interaction.\n",
"bugtrack_url": null,
"license": null,
"summary": "An ASGI FHIR API framework built on top of FastAPI and FHIR Resources",
"version": "2.4.2",
"project_urls": {
"Homepage": "https://github.com/canvas-medical/fhirstarter",
"Repository": "https://github.com/canvas-medical/fhirstarter"
},
"split_keywords": [
"fhir",
" api",
" server",
" resources",
" framework",
" fastapi",
" healthcare",
" hl7"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "046460eafddc4f2747a5181d044dbf5fc454f3bdfd831495e348c79cc00870b4",
"md5": "c11fc76eea1dda95e717f094f49b9521",
"sha256": "bcd4204d3c4255f9809b0ee9889757db0f1f3f2dc65ce3b9b641fe0016955987"
},
"downloads": -1,
"filename": "fhirstarter-2.4.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c11fc76eea1dda95e717f094f49b9521",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<3.13.0,>=3.8.0",
"size": 2050012,
"upload_time": "2024-10-15T22:23:23",
"upload_time_iso_8601": "2024-10-15T22:23:23.397594Z",
"url": "https://files.pythonhosted.org/packages/04/64/60eafddc4f2747a5181d044dbf5fc454f3bdfd831495e348c79cc00870b4/fhirstarter-2.4.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "fb66b6949a777510e1bb0c54a413d70a9753d9db6f9be0a01eb47fa1b029faaf",
"md5": "d64796313d411ae87b3ad40978af2c40",
"sha256": "a0a3949dfb683f6de475021e10e9384bce07e9c20f0bdfeccf6022f9bb9f122c"
},
"downloads": -1,
"filename": "fhirstarter-2.4.2.tar.gz",
"has_sig": false,
"md5_digest": "d64796313d411ae87b3ad40978af2c40",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<3.13.0,>=3.8.0",
"size": 2041077,
"upload_time": "2024-10-15T22:23:25",
"upload_time_iso_8601": "2024-10-15T22:23:25.936846Z",
"url": "https://files.pythonhosted.org/packages/fb/66/b6949a777510e1bb0c54a413d70a9753d9db6f9be0a01eb47fa1b029faaf/fhirstarter-2.4.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-15 22:23:25",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "canvas-medical",
"github_project": "fhirstarter",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "fhirstarter"
}