MCPcopy Index your code
hub / github.com/FlixCoder/fhir-sdk

github.com/FlixCoder/fhir-sdk @v0.16.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.16.0 ↗ · + Follow
959 symbols 1,404 edges 69 files 147 documented · 15%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

FHIR SDK

crates.io page docs.rs page license: MIT

This is a FHIR (client) library. The models are generated from the FHIR StructureDefinitions (see FHIR downloads). It aims to be:

  • fully compliant
  • as safe as possibe
  • as easy to use as possible
  • fully featured

Features

  • [x] Generated FHIR codes, types and resources
  • [x] Serialization and deserialization to and from JSON
  • [x] Optional builders for types and resources
  • [x] Implementation of base traits
  • [x] (Base)Resource for accessing common fields
  • [x] NamedResource for getting the resource type in const time
  • [x] DomainResource for accessing common fields
  • [x] IdentifiableResource for all resources with an identifier field
  • [x] Client implementation
  • [x] Create, Read, Update, Delete
  • [x] Search + Paging
  • [x] Batch operations / Transactions
  • [x] Operations
  • [x] Patch
  • [x] Authentication
  • [ ] GraphQL
  • [ ] FHIRpath implementation
  • [ ] Resource validation using FHIRpath and regular expressions

Not Planned

Example

use fhir_sdk::r5::resources::Patient;
use fhir_sdk::client::{*, r5::*};
use fhir_sdk::{TryStreamExt, header};

#[tokio::main]
async fn main() -> Result<(), Error> {
    // Set up the client using the local test server.
    let settings = RequestSettings::default()
        .header(header::AUTHORIZATION, "Bearer <token>".parse().unwrap());
    let client = Client::builder()
        .base_url("http://localhost:8100/fhir/".parse().unwrap())
        .request_settings(settings)
        .build()?;

    // Create a Patient resource using a builder.
    let mut patient = Patient::builder().active(false).build().unwrap();
    // Push it to the server.
    patient.create(&client).await?;
    // The id and versionId is updated automatically this way.
    assert!(patient.id.is_some());

    // Search for all patient with `active` = false, including pagination.
    let patients: Vec<Patient> = client
        .search(SearchParameters::empty().and(TokenSearch::Standard {
            name: "active",
            system: None,
            code: Some("false"),
            not: false,
        }))
        .await?
        .all_matches()
        .try_collect()
        .await?;

    Ok(())
}

For more examples, see the tests or below.

Development & Testing

  1. Install cargo-make: cargo install cargo-make.
  2. From the workspace root directory, you can run the following tasks:
    • Format code: cargo make format
    • Check formatting: cargo make formatting
    • Run docker environment for all FHIR versions: cargo make docker-ci-up
    • Run all tests: cargo make test
    • Stop docker environment for all FHIR versions: cargo make docker-ci-down
    • Run docker env, run all tests, stop docker env: cargo make ci-tests
    • Run clippy for all feature sets, failing on any warnings: cargo make clippy
    • Do all checks that are done in CI: cargo make ci
    • Run code generator: cargo make generate

Known Problems

  • The compile time and its memory usage are really high. This is due to the big serde derives being highly generic. It might be possible to shave some off by manually implementing Deserialize and Serialize, but that is complex.
  • Vec<Option<T>> is annoying, but sadly is required to allow [null, {...}, null] for using FHIR resources with extensions..
  • It is not supported to replace required fields by an extension.

More examples

Reading a resource from string/file

use fhir_sdk::r5::resources::Resource;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resource_str = r#"{
        "resourceType": "Patient",
        "id": "my-id",
        "birthDate": "2024-01-01"
    }"#;
    let _resource: Resource = serde_json::from_str(resource_str)?;
    Ok(())
}

Re-Authentication via callback

use fhir_sdk::r5::resources::Patient;
use fhir_sdk::client::*;
use fhir_sdk::{HttpClient, HeaderValue};

/// Gets called whenever there is an UNAUTHORIZED response.
/// Retries the response with the new Authorization header.
async fn my_auth_callback(client: HttpClient) -> neuer_error::Result<HeaderValue> {
    let _response = client.get("my-url").send().await?;
    Ok(HeaderValue::from_static("Bearer <token>"))
}

/// Same as above, but with state.
struct MyLogin {
    valid: std::time::Instant,
}

impl LoginManager for MyLogin {
    type Error = neuer_error::NeuErr;

    async fn authenticate(&mut self, client: HttpClient) -> Result<HeaderValue, Self::Error> {
        if self.valid.elapsed().as_secs() > 360 {
            let _response = client.get("my-url").send().await?;
            self.valid = std::time::Instant::now();
        }
        Ok(HeaderValue::from_static("Bearer <token>"))
    }
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    // Set up the client using the local test server.
    let client = Client::builder()
        .base_url("http://localhost:8100/fhir/".parse().unwrap())
        // Register async fn as callback.
        .auth_callback(my_auth_callback)
        // Register struct with state. Overwrites previous callback.
        .auth_callback(MyLogin { valid: std::time::Instant::now() })
        // Register async closure. Overwrites previous callback.
        .auth_callback(|_client: HttpClient| async move { neuer_error::Ok(HeaderValue::from_static("hi")) })
        .build()?;

    // Create a Patient resource using a builder.
    let mut patient = Patient::builder().active(false).build().unwrap();
    // Push it to the server. On unauthorized failures, the client will call our
    // auth_callback method to refresh the authorization.
    patient.create(&client).await?;

    Ok(())
}

Resource identifier access

use fhir_sdk::r5::{
    codes::AdministrativeGender,
    resources::{IdentifiableResource, Patient},
    types::{Identifier, HumanName},
};

#[tokio::main]
async fn main() {
    // Create a Patient resource using a builder.
    let mut patient = Patient::builder()
        .active(false)
        .identifier(vec![Some(
            Identifier::builder()
                .system("MySystem".to_owned())
                .value("ID".to_owned())
                .build()
                .unwrap()
        )])
        .gender(AdministrativeGender::Male)
        .name(vec![Some(HumanName::builder().family("Test".to_owned()).build().unwrap())])
        .build()
        .unwrap();

    // Check the identifier value.
    assert_eq!(patient.identifier_with_system("MySystem").map(String::as_str), Some("ID"));
}

Minimum supported Rust version

Currently, I am always using the latest Rust version and do not put in any effort to keep the MSRV. Please open an issue in case you need a different policy, I might consider changing the policy.

License

Licensed under the MIT license. All contributors agree to license under this license.

Extension points exported contracts — how you extend this code

SearchParameter (Interface)
Functionality to convert a SearchParameter to the URL query. [8 implementers]
crates/fhir-sdk/src/client/search.rs
Sealed (Interface)
Utils for internal use (outside client). Crate internal trait to disallow users to implement some trait externally. Do n [3 …
crates/fhir-sdk/src/utils.rs
LoginManager (Interface)
Trait to be implemented for the [`ClientBuilder::auth_callback`](super::builder::ClientBuilder::auth_callback). You can [3 …
crates/fhir-sdk/src/client/auth.rs
ResourceWrite (Interface)
A trait to write resources to the FHIR server, mutating the interior id and version_id so that the resource is up to dat [1 …
crates/fhir-sdk/src/client/fhir/write.rs
FhirVersion (Interface)
FHIR version type "marker", but with additional information. Only implemented if "builders" feature is activated.
crates/fhir-sdk/src/version.rs

Core symbols most depended-on inside this repo

deserialize
called by 273
crates/fhir-model/src/date_time.rs
clone
called by 47
crates/fhir-sdk/src/client/mod.rs
clone
called by 30
crates/fhir-sdk/src/client/fhir/paging.rs
insert
called by 27
crates/fhir-sdk/src/client/fhir/patch.rs
build
called by 24
crates/fhir-sdk/src/client/builder.rs
header
called by 23
crates/fhir-sdk/src/client/request.rs
url
called by 19
crates/fhir-sdk/src/client/mod.rs
run_request
called by 13
crates/fhir-sdk/src/client/mod.rs

Shape

Enum 390
Class 315
Method 145
Function 92
Interface 17

Languages

Rust100%

Modules by API surface

crates/fhir-model/src/stu3/codes/generated.rs275 symbols
crates/fhir-model/src/r5/types/generated.rs147 symbols
crates/fhir-model/src/r4b/types/generated.rs137 symbols
crates/fhir-model/src/stu3/types/generated.rs123 symbols
crates/fhir-sdk/tests/client-medplum-r4.rs18 symbols
crates/fhir-sdk/src/client/mod.rs18 symbols
crates/fhir-sdk/src/client/fhir/crud.rs18 symbols
crates/fhir-sdk/src/client/fhir/paging.rs17 symbols
crates/generate/src/model/structures.rs14 symbols
crates/fhir-sdk/src/client/fhir/patch.rs12 symbols
crates/fhir-sdk/src/client/builder.rs12 symbols
crates/fhir-model/src/date_time.rs12 symbols

Datastores touched

hapi_r4bDatabase · 1 repos
hapi_r5Database · 1 repos
hapi_stu3Database · 1 repos

For agents

$ claude mcp add fhir-sdk \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact