GCP-managed FHIR backend (плюс DICOM, HL7v2 — нам не критично). Альтернативы — hapi-fhir (self-hosted), Azure FHIR, Medplum. Выбран в session 871a7608 как primary FHIR storage для BloodGPT multi-tenant.

Структура

projects/{proj}/locations/{loc}/datasets/{datasetId}/fhirStores/{storeId}/fhir/{resourceType}/{id}
  • Dataset — top-level container, изолированный namespace
  • FHIR Store — внутри dataset, attached к FHIR R4/R5 версии
  • Auth — OAuth2 через Workload Identity (GKE → GCP). Tokens истекают каждый час, нужен refresh per-call.

Использование в BloodGPT

  • One dataset per Organizationdataset: org-{orgId}, fhirStore: fhir-r4. Физическая изоляция tenants. См. multi-tenant-fhir-storage.
  • Provisioning — Inngest function на event org/created создаёт dataset + FHIR store + Organization/bloodgpt (изначально планировался Device/bloodgpt-ai, теперь Organization). Provisioning только для новых orgs — existing prod datasets без Organization/bloodgpt дают runtime error при write AI-content; manual fix через gcloud сделан только для двух (см. fhir-organization).
  • HIPAA — BAA на GCP автоматически покрывает Healthcare API + Cloud SQL + GCS + GKE.

Capabilities

  • FHIR R4 / R5 поддержка
  • Built-in audit logging (Cloud Audit Logs)
  • BigQuery streaming (для будущей analytics — стрим из FHIR в BQ)
  • CMEK encryption
  • VPC Service Controls (perimeter defense)
  • Search API стандартный FHIR (R4 SearchParameter)
  • Conditional create через ifNoneExist

Gotchas / ограничения

Device в author[x] — отвергается

Verified в session c9560637 (Feb 17 2026): в author[x] choice-type Healthcare API принимает только Practitioner | Patient | RelatedPerson | Organization. Device — отвергается с validation failure. Это vendor-specific ограничение — FHIR R4 spec разрешает Device через author[x]. Pivot на fhir-organization (Organization-as-author). См. authorship-organization-not-device.

Ключевой урок: vendor-feasibility валидируется до finalize design decisions, не пост-фактум. См. multi-tenant-fhir-storage open question .

_include:iterate=Observation:has-member — поддерживается

Verified в c9560637. Google explicit declaration о поддержке :iterate modifier для R4/R5. Это значит Panel Observations с hasMember возвращаются в одном bundle:

GET DiagnosticReport?patient={pid}&date={date}
  &_include=DiagnosticReport:result
  &_include:iterate=Observation:has-member

Один запрос — DiagnosticReport → Panel Observations → Child Observations.

date search param на DiagnosticReport ищет по issued, не effectiveDateTime

Verified в c9560637 через эксперимент: ?date=2026-02-18 дал пустой результат, потому что issued: "2026-02-18", а кровь сдавали effectiveDateTime: "2025-12-18". Если нужно искать по дате забора — использовать другой параметр (effective) или search через _filter. Критично для use case “вернуть отчёт по дате забора”.

Patient/$everything работает, но возвращает много

Verified в c9560637: одна ваза анализа → 5279 строк JSON в $everything bundle. Не selective — возвращает всё связанное с Patient. Для use case “вернуть конкретный отчёт за дату” — overkill, нужен более точный запрос (например, DiagnosticReport?patient&date&_include).

Latency

FHIR search ~50-200ms vs SQL ~2-5ms. Для нашего pipeline (LLM-генерация секунды) это шум. Но для dashboard read-paths без LLM — может стать заметно. Mitigation strategy не финализирована (см. multi-tenant-fhir-storage).

OAuth refresh

Tokens из Workload Identity истекают через час. Subclass HealthcareFhirClient (extends FhirClient) refresh-ит per-call. Это потребовало перевести request, headers, baseUrl в base FhirClient class из private в protected.

Связано

Источники

Источники: 1 2 3 4.

Сноски

  1. GCP Healthcare API docs, accessed 2026-05-17, https://cloud.google.com/healthcare-api/docs.

  2. BAA on GCP, accessed 2026-05-17, https://cloud.google.com/security/compliance/hipaa-compliance.

  3. Сессия ildar/871a7608, 2026-02-13 — ` (architecture decision).

  4. Сессия ildar/c9560637, 2026-02-17 — ` (Device limit verified.