วิธีใช้ HL7 FHIR API: คู่มือการเชื่อมต่อระบบสุขภาพฉบับสมบูรณ์ (ปี 2026)

Ashley Innocent

Ashley Innocent

25 March 2026

วิธีใช้ HL7 FHIR API: คู่มือการเชื่อมต่อระบบสุขภาพฉบับสมบูรณ์ (ปี 2026)

Apidog สำหรับองค์กร

ติดตั้งภายในองค์กร

SSO & RBAC

รองรับ SOC 2

สำรวจ Apidog Enterprise

สรุป

HL7 FHIR (Fast Healthcare Interoperability Resources) เป็นมาตรฐานที่ทันสมัยสำหรับการแลกเปลี่ยนข้อมูลด้านการดูแลสุขภาพ โดยใช้ RESTful API พร้อมการตอบกลับแบบ JSON/XML มันมีทรัพยากรที่ได้มาตรฐานสำหรับผู้ป่วย ข้อมูลสังเกตการณ์ ยา และอื่นๆ พร้อมการยืนยันตัวตนด้วย OAuth 2.0 และ SMART on FHIR สำหรับการผสานรวมแอปพลิเคชัน คู่มือนี้ครอบคลุมสถาปัตยกรรม FHIR ประเภททรัพยากร พารามิเตอร์การค้นหา การยืนยันตัวตน และกลยุทธ์การนำไปใช้งานจริง

บทนำ

การแตกแยกของข้อมูลด้านการดูแลสุขภาพทำให้ระบบดูแลสุขภาพของสหรัฐอเมริกาต้องเสียค่าใช้จ่าย 30 พันล้านดอลลาร์ต่อปี สำหรับนักพัฒนาที่สร้างแอปพลิเคชันด้านการดูแลสุขภาพ การผสานรวม HL7 FHIR API ไม่ใช่ทางเลือก—แต่เป็นมาตรฐานอุตสาหกรรมที่ CMS กำหนดและนำไปใช้โดย Epic, Cerner และผู้จำหน่าย EHR รายใหญ่ทั้งหมด

นี่คือความจริง: ผู้ให้บริการที่ใช้แอปที่รองรับ FHIR จะลดเวลาในการประสานงานการดูแลลง 40% และกำจัดการร้องขอเวชระเบียนที่ใช้แฟกซ์ถึง 85% การผสานรวม FHIR API ที่แข็งแกร่งช่วยให้การแลกเปลี่ยนข้อมูลเป็นไปอย่างราบรื่นระหว่าง EHRs, พอร์ทัลผู้ป่วย และแพลตฟอร์มการประสานงานการดูแล

คู่มือนี้จะอธิบายกระบวนการผสานรวม HL7 FHIR API ทั้งหมด คุณจะได้เรียนรู้สถาปัตยกรรม FHIR, ประเภททรัพยากร, พารามิเตอร์การค้นหา, การยืนยันตัวตน OAuth 2.0, การผสานรวม SMART on FHIR และกลยุทธ์การปรับใช้ในสภาพแวดล้อมจริง ในท้ายที่สุด คุณจะมีการผสานรวม FHIR ที่พร้อมใช้งานในสภาพแวดล้อมจริง

💡
Apidog ทำให้การผสานรวม API ด้านการดูแลสุขภาพง่ายขึ้น ทดสอบ FHIR endpoint, ตรวจสอบ schema ของทรัพยากร, แก้ไขปัญหา flow การยืนยันตัวตน และจัดทำเอกสารข้อกำหนด API ในที่ทำงานเดียว นำเข้า FHIR Implementation Guides, จำลองการตอบกลับ และแบ่งปันสถานการณ์การทดสอบกับทีมของคุณ
ปุ่ม

HL7 FHIR คืออะไร?

FHIR (Fast Healthcare Interoperability Resources) เป็นกรอบงานมาตรฐานสำหรับการแลกเปลี่ยนข้อมูลด้านการดูแลสุขภาพทางอิเล็กทรอนิกส์ พัฒนาโดย Health Level Seven International (HL7), FHIR ใช้เทคโนโลยีเว็บสมัยใหม่ รวมถึง RESTful API, JSON, XML และ OAuth 2.0

รูปภาพ

ประเภททรัพยากร FHIR

FHIR กำหนดประเภททรัพยากรมากกว่า 140 รายการ ทรัพยากรหลักได้แก่:

ทรัพยากร (Resource) วัตถุประสงค์ (Purpose) กรณีการใช้งานทั่วไป (Common Use Cases)
Patient ข้อมูลประชากร การค้นหาผู้ป่วย, การลงทะเบียน
Practitioner ข้อมูลผู้ให้บริการ ไดเรกทอรี, การจัดตารางเวลา
Encounter การเข้าพบ/การรับเข้า ตอนการดูแล, การเรียกเก็บเงิน
Observation ข้อมูลทางคลินิก สัญญาณชีพ, ผลตรวจทางห้องปฏิบัติการ, การประเมิน
Condition ปัญหา/การวินิจฉัย รายการปัญหา, การวางแผนการดูแล
MedicationRequest ใบสั่งยา การสั่งยาทางอิเล็กทรอนิกส์, ประวัติการใช้ยา
AllergyIntolerance อาการแพ้ การตรวจสอบความปลอดภัย, การแจ้งเตือน
Immunization การฉีดวัคซีน ประวัติการฉีดวัคซีน
DiagnosticReport รายงานห้องปฏิบัติการ/ภาพวินิจฉัย การส่งผลลัพธ์
DocumentReference เอกสารทางคลินิก CCD, สรุปการจำหน่าย

สถาปัตยกรรม FHIR API

FHIR ใช้โครงสร้าง RESTful API:

https://fhir-server.com/fhir/{resourceType}/{id}

เปรียบเทียบเวอร์ชัน FHIR

เวอร์ชัน (Version) สถานะ (Status) กรณีการใช้งาน (Use Case)
R4 (4.0.1) STU ปัจจุบัน การนำไปใช้งานจริง
R4B (4.3) Trial Implementation ผู้ใช้เริ่มต้น
R5 (5.0.0) Draft STU การนำไปใช้งานในอนาคต
DSTU2 เลิกใช้แล้ว เฉพาะระบบเดิมเท่านั้น

CMS กำหนดให้ EHRs ที่ได้รับการรับรองต้องรองรับ FHIR R4 สำหรับ Patient Access และ Provider Access APIs

เริ่มต้นใช้งาน: การเข้าถึงเซิร์ฟเวอร์ FHIR

ขั้นตอนที่ 1: เลือกเซิร์ฟเวอร์ FHIR ของคุณ

ตัวเลือกสำหรับการปรับใช้เซิร์ฟเวอร์ FHIR:

เซิร์ฟเวอร์ (Server) ประเภท (Type) ค่าใช้จ่าย (Cost) เหมาะที่สุดสำหรับ (Best For)
Azure API for FHIR Managed จ่ายตามการใช้งาน ระดับองค์กร, ลูกค้า Azure
AWS HealthLake Managed จ่ายตามการใช้งาน สภาพแวดล้อม AWS
Google Cloud Healthcare API Managed จ่ายตามการใช้งาน สภาพแวดล้อม GCP
HAPI FHIR โอเพนซอร์ส โฮสต์เอง การปรับใช้แบบกำหนดเอง
Epic FHIR Server เชิงพาณิชย์ ลูกค้า Epic การผสานรวม Epic EHR
Cerner Ignite FHIR เชิงพาณิชย์ ลูกค้า Cerner การผสานรวม Cerner EHR

ขั้นตอนที่ 2: รับข้อมูลรับรองเซิร์ฟเวอร์

สำหรับบริการ FHIR บนคลาวด์:

# Azure API for FHIR
# 1. สร้างบริการ FHIR ใน Azure Portal
# 2. กำหนดค่าการตรวจสอบสิทธิ์ (OAuth 2.0 หรือ AAD)
# 3. รับ FHIR endpoint: https://{service-name}.azurehealthcareapis.com
# 4. ลงทะเบียนแอปไคลเอนต์สำหรับ OAuth

# AWS HealthLake
# 1. สร้าง Data Store ใน AWS Console
# 2. กำหนดค่าบทบาท IAM
# 3. รับ endpoint: https://healthlake.{region}.amazonaws.com

ขั้นตอนที่ 3: ทำความเข้าใจการดำเนินการ RESTful ของ FHIR

FHIR รองรับเมธอด HTTP มาตรฐาน:

การดำเนินการ (Operation) เมธอด HTTP (HTTP Method) Endpoint คำอธิบาย (Description)
Read GET /{resourceType}/{id} รับทรัพยากรที่เจาะจง
Search GET /{resourceType}?param=value ค้นหาทรัพยากร
Create POST /{resourceType} สร้างทรัพยากรใหม่
Update PUT /{resourceType}/{id} แทนที่ทรัพยากร
Patch PATCH /{resourceType}/{id} อัปเดตบางส่วน
Delete DELETE /{resourceType}/{id} ลบทรัพยากร
History GET /{resourceType}/{id}/_history เวอร์ชันของทรัพยากร

ขั้นตอนที่ 4: เรียกใช้ FHIR ครั้งแรกของคุณ

ทดสอบการเชื่อมต่อ:

curl -X GET "https://fhir-server.com/fhir/metadata" \
  -H "Accept: application/fhir+json" \
  -H "Authorization: Bearer {token}"

การตอบกลับที่คาดหวัง:

{
  "resourceType": "CapabilityStatement",
  "status": "active",
  "date": "2026-03-25",
  "fhirVersion": "4.0.1",
  "rest": [{
    "mode": "server",
    "resource": [
      { "type": "Patient" },
      { "type": "Observation" },
      { "type": "Condition" }
    ]
  }]
}

การดำเนินการหลักของ FHIR

การอ่านทรัพยากรผู้ป่วย

ดึงข้อมูลผู้ป่วยด้วย ID:

const FHIR_BASE_URL = process.env.FHIR_BASE_URL;
const FHIR_TOKEN = process.env.FHIR_TOKEN;

const fhirRequest = async (endpoint, options = {}) => {
  const response = await fetch(`${FHIR_BASE_URL}/fhir${endpoint}`, {
    ...options,
    headers: {
      'Accept': 'application/fhir+json',
      'Authorization': `Bearer ${FHIR_TOKEN}`,
      ...options.headers
    }
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`FHIR Error: ${error.issue?.[0]?.details?.text || response.statusText}`);
  }

  return response.json();
};

// อ่านข้อมูลผู้ป่วยด้วย ID
const getPatient = async (patientId) => {
  const patient = await fhirRequest(`/Patient/${patientId}`);
  return patient;
};

// การใช้งาน
const patient = await getPatient('12345');
console.log(`ผู้ป่วย: ${patient.name[0].given[0]} ${patient.name[0].family}`);
console.log(`วันเกิด: ${patient.birthDate}`);
console.log(`เพศ: ${patient.gender}`);

โครงสร้างทรัพยากรผู้ป่วย

{
  "resourceType": "Patient",
  "id": "12345",
  "identifier": [
    {
      "use": "usual",
      "type": {
        "coding": [{
          "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
          "code": "MR"
        }]
      },
      "system": "http://hospital.example.org",
      "value": "MRN123456"
    }
  ],
  "name": [
    {
      "use": "official",
      "family": "Smith",
      "given": ["John", "Michael"]
    }
  ],
  "telecom": [
    {
      "system": "phone",
      "value": "555-123-4567",
      "use": "home"
    },
    {
      "system": "email",
      "value": "john.smith@email.com"
    }
  ],
  "gender": "male",
  "birthDate": "1985-06-15",
  "address": [
    {
      "use": "home",
      "line": ["123 Main Street"],
      "city": "Springfield",
      "state": "IL",
      "postalCode": "62701"
    }
  ]
}

การค้นหาทรัพยากร

ค้นหาผู้ป่วยด้วยชื่อ:

const searchPatients = async (searchParams) => {
  const query = new URLSearchParams();

  // เพิ่มพารามิเตอร์การค้นหา
  if (searchParams.name) {
    query.append('name', searchParams.name);
  }
  if (searchParams.birthDate) {
    query.append('birthdate', searchParams.birthDate);
  }
  if (searchParams.identifier) {
    query.append('identifier', searchParams.identifier);
  }
  if (searchParams.gender) {
    query.append('gender', searchParams.gender);
  }

  const response = await fhirRequest(`/Patient?${query.toString()}`);
  return response;
};

// การใช้งาน
const results = await searchPatients({ name: 'Smith', birthDate: '1985-06-15' });

console.log(`พบผู้ป่วย ${results.total} ราย`);
results.entry.forEach(entry => {
  const patient = entry.resource;
  console.log(`${patient.name[0].family}, ${patient.name[0].given[0]}`);
});

พารามิเตอร์การค้นหาที่พบบ่อย

ทรัพยากร (Resource) พารามิเตอร์การค้นหา (Search Parameters) ตัวอย่าง (Example)
Patient name, birthdate, identifier, gender, phone, email ?name=Smith&birthdate=1985-06-15
Observation patient, code, date, category, status ?patient=123&code=8480-6&date=ge2026-01-01
Condition patient, clinical-status, category, onset-date ?patient=123&clinical-status=active
MedicationRequest patient, status, intent, medication ?patient=123&status=active
Encounter patient, date, status, class ?patient=123&date=ge2026-01-01
DiagnosticReport patient, category, date, status ?patient=123&category=laboratory

ตัวปรับแต่งการค้นหา

ตัวปรับแต่ง (Modifier) คำอธิบาย (Description) ตัวอย่าง (Example)
:exact ตรงกันทุกประการ name:exact=Smith
:contains มีส่วนประกอบ name:contains=smi
:missing มี/ไม่มีค่า phone:missing=true
: (คำนำหน้า) ตัวดำเนินการคำนำหน้า birthdate=ge1980-01-01

คำนำหน้าการค้นหาสำหรับวันที่และตัวเลข

คำนำหน้า (Prefix) ความหมาย (Meaning) ตัวอย่าง (Example)
eq เท่ากับ birthdate=eq1985-06-15
ne ไม่เท่ากับ birthdate=ne1985-06-15
gt มากกว่า birthdate=gt1980-01-01
lt น้อยกว่า birthdate=lt1990-01-01
ge มากกว่าหรือเท่ากับ birthdate=ge1980-01-01
le น้อยกว่าหรือเท่ากับ birthdate=le1990-01-01
sa เริ่มต้นหลังจาก date=sa2026-01-01
eb สิ้นสุดก่อน date=eb2026-12-31

การทำงานกับข้อมูลทางคลินิก

การสร้าง Observation (สัญญาณชีพ)

บันทึกสัญญาณชีพ:

const createObservation = async (observationData) => {
  const observation = {
    resourceType: 'Observation',
    status: 'final',
    category: [
      {
        coding: [{
          system: 'http://terminology.hl7.org/CodeSystem/observation-category',
          code: 'vital-signs'
        }]
      }
    ],
    code: {
      coding: [{
        system: 'http://loinc.org',
        code: observationData.code, // เช่น '8480-6' สำหรับ Systolic BP
        display: observationData.display
      }]
    },
    subject: {
      reference: `Patient/${observationData.patientId}`
    },
    effectiveDateTime: observationData.effectiveDate || new Date().toISOString(),
    valueQuantity: {
      value: observationData.value,
      unit: observationData.unit,
      system: 'http://unitsofmeasure.org',
      code: observationData.ucumCode
    },
    performer: [
      {
        reference: `Practitioner/${observationData.performerId}`
      }
    ]
  };

  const response = await fhirRequest('/Observation', {
    method: 'POST',
    body: JSON.stringify(observation)
  });

  return response;
};

// การใช้งาน - บันทึกความดันโลหิต
const systolicBP = await createObservation({
  patientId: '12345',
  code: '8480-6',
  display: 'ความดันโลหิตซิสโตลิก',
  value: 120,
  unit: 'mmHg',
  ucumCode: 'mm[Hg]',
  performerId: '67890'
});

console.log(`Observation สร้างแล้ว: ${systolicBP.id}`);

รหัส LOINC ที่พบบ่อย

รหัส (Code) การแสดงผล (Display) หมวดหมู่ (Category)
8480-6 ความดันโลหิตซิสโตลิก สัญญาณชีพ
8462-4 ความดันโลหิตไดแอสโตลิก สัญญาณชีพ
8867-4 อัตราการเต้นของหัวใจ สัญญาณชีพ
8310-5 อุณหภูมิร่างกาย สัญญาณชีพ
8302-2 ส่วนสูงของร่างกาย สัญญาณชีพ
29463-7 น้ำหนักตัว สัญญาณชีพ
8871-5 อัตราการหายใจ สัญญาณชีพ
2339-0 กลูโคส [มวล/ปริมาตร] ห้องปฏิบัติการ
4548-4 ฮีโมโกลบิน A1c ห้องปฏิบัติการ
2093-3 คอเลสเตอรอล [มวล/ปริมาตร] ห้องปฏิบัติการ

การสร้าง Condition (รายการปัญหา)

เพิ่มการวินิจฉัยลงในรายการปัญหา:

const createCondition = async (conditionData) => {
  const condition = {
    resourceType: 'Condition',
    clinicalStatus: {
      coding: [{
        system: 'http://terminology.hl7.org/CodeSystem/condition-clinical',
        code: conditionData.status || 'active'
      }]
    },
    verificationStatus: {
      coding: [{
        system: 'http://terminology.hl7.org/CodeSystem/condition-ver-status',
        code: 'confirmed'
      }]
    },
    category: [
      {
        coding: [{
          system: 'http://terminology.hl7.org/CodeSystem/condition-category',
          code: conditionData.category || 'problem-list-item'
        }]
      }
    ],
    code: {
      coding: [{
        system: 'http://snomed.info/sct',
        code: conditionData.sctCode,
        display: conditionData.display
      }]
    },
    subject: {
      reference: `Patient/${conditionData.patientId}`
    },
    onsetDateTime: conditionData.onsetDate,
    recordedDate: new Date().toISOString()
  };

  const response = await fhirRequest('/Condition', {
    method: 'POST',
    body: JSON.stringify(condition)
  });

  return response;
};

// การใช้งาน - เพิ่มโรคเบาหวานลงในรายการปัญหา
const diabetes = await createCondition({
  patientId: '12345',
  sctCode: '44054006',
  display: 'โรคเบาหวานชนิดที่ 2',
  status: 'active',
  category: 'problem-list-item',
  onsetDate: '2024-01-15'
});

รหัส SNOMED CT ที่พบบ่อย

รหัส (Code) การแสดงผล (Display) หมวดหมู่ (Category)
44054006 โรคเบาหวานชนิดที่ 2 ปัญหา
38341003 ความดันโลหิตสูง ปัญหา
195967001 โรคหอบหืด ปัญหา
13645005 โรคปอดอุดกั้นเรื้อรัง ปัญหา
35489007 โรคซึมเศร้า ปัญหา
22298006 กล้ามเนื้อหัวใจตาย ปัญหา
26929004 โรคอัลไซเมอร์ ปัญหา
396275006 โรคข้อเข่าเสื่อม ปัญหา

การดึงข้อมูลยาของผู้ป่วย

รับคำขอใช้ยาที่ใช้งานอยู่:

const getPatientMedications = async (patientId) => {
  const response = await fhirRequest(
    `/MedicationRequest?patient=${patientId}&status=active`
  );

  return response;
};

// การใช้งาน
const medications = await getPatientMedications('12345');

medications.entry?.forEach(entry => {
  const med = entry.resource;
  console.log(`${med.medicationCodeableConcept.coding[0].display}`);
  console.log(`  ขนาดยา: ${med.dosageInstruction[0]?.text}`);
  console.log(`  สถานะ: ${med.status}`);
});

การดึงผลตรวจทางห้องปฏิบัติการ

รับรายงานการวินิจฉัยและข้อมูลสังเกตการณ์:

const getPatientLabResults = async (patientId, options = {}) => {
  const params = new URLSearchParams({
    patient: patientId,
    category: options.category || 'laboratory'
  });

  if (options.dateFrom) {
    params.append('date', `ge${options.dateFrom}`);
  }

  const response = await fhirRequest(`/DiagnosticReport?${params.toString()}`);
  return response;
};

// รับการตรวจทางห้องปฏิบัติการที่เฉพาะเจาะจง (เช่น HbA1c)
const getLabValue = async (patientId, loincCode) => {
  const params = new URLSearchParams({
    patient: patientId,
    code: loincCode
  });

  const response = await fhirRequest(`/Observation?${params.toString()}`);
  return response;
};

// การใช้งาน - รับผล HbA1c
const hba1c = await getLabValue('12345', '4548-4');
hba1c.entry?.forEach(entry => {
  const obs = entry.resource;
  console.log(`HbA1c: ${obs.valueQuantity.value} ${obs.valueQuantity.unit}`);
  console.log(`วันที่: ${obs.effectiveDateTime}`);
});

OAuth 2.0 และ SMART on FHIR

ทำความเข้าใจการยืนยันตัวตน FHIR

เซิร์ฟเวอร์ FHIR ใช้ OAuth 2.0 ร่วมกับ OpenID Connect:

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   ไคลเอนต์   │───▶│   เซิร์ฟเวอร์   │───▶│   เซิร์ฟเวอร์   │
│    (แอป)    │    │  ตรวจสอบสิทธิ์  │    │      FHIR     │
└─────────────┘    └─────────────┘    └─────────────┘
     │                    │                    │
     │  1. คำขอตรวจสอบสิทธิ์   │                    │
     │───────────────────▶│                    │
     │                    │                    │
     │  2. ผู้ใช้เข้าสู่ระบบ    │                    │
     │◀───────────────────│                    │
     │                    │                    │
     │  3. รหัสตรวจสอบสิทธิ์   │                    │
     │───────────────────▶│                    │
     │                    │                    │
     │  4. คำขอโทเค็น     │                    │
     │───────────────────▶│                    │
     │                    │  5. โทเค็น + ID    │
     │◀───────────────────│                    │
     │                    │                    │
     │  6. คำขอ API      │                    │
     │────────────────────────────────────────▶│
     │                    │                    │
     │  7. ข้อมูล FHIR    │                    │
     │◀────────────────────────────────────────│

การเปิดแอป SMART on FHIR

ใช้งานการเปิดแอป SMART:

const crypto = require('crypto');

class SMARTClient {
  constructor(config) {
    this.clientId = config.clientId;
    this.redirectUri = config.redirectUri;
    this.issuer = config.issuer; // URL เซิร์ฟเวอร์ FHIR
    this.scopes = config.scopes;
  }

  generatePKCE() {
    const codeVerifier = crypto.randomBytes(32).toString('base64url');
    const codeChallenge = crypto
      .createHash('sha256')
      .update(codeVerifier)
      .digest('base64url');

    return { codeVerifier, codeChallenge };
  }

  buildAuthUrl(state, patientId = null) {
    const { codeVerifier, codeChallenge } = this.generatePKCE();

    // เก็บ codeVerifier สำหรับการแลกเปลี่ยนโทเค็น
    this.codeVerifier = codeVerifier;

    const params = new URLSearchParams({
      response_type: 'code',
      client_id: this.clientId,
      redirect_uri: this.redirectUri,
      scope: this.scopes.join(' '),
      state: state,
      code_challenge: codeChallenge,
      code_challenge_method: 'S256',
      aud: this.issuer,
      launch: patientId ? `patient-${patientId}` : null
    });

    return `${this.issuer}/authorize?${params.toString()}`;
  }

  async exchangeCodeForToken(code) {
    const response = await fetch(`${this.issuer}/token`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: new URLSearchParams({
        grant_type: 'authorization_code',
        code: code,
        redirect_uri: this.redirectUri,
        client_id: this.clientId,
        code_verifier: this.codeVerifier
      })
    });

    const data = await response.json();

    return {
      accessToken: data.access_token,
      refreshToken: data.refresh_token,
      expiresIn: data.expires_in,
      patientId: data.patient,
      encounterId: data.encounter
    };
  }
}

// การใช้งาน
const smartClient = new SMARTClient({
  clientId: 'my-app-client-id',
  redirectUri: 'https://myapp.com/callback',
  issuer: 'https://fhir.epic.com',
  scopes: [
    'openid',
    'profile',
    'patient/Patient.read',
    'patient/Observation.read',
    'patient/Condition.read',
    'patient/MedicationRequest.read',
    'offline_access'
  ]
});

// เปลี่ยนเส้นทางผู้ใช้ไปยัง URL การตรวจสอบสิทธิ์
const state = crypto.randomBytes(16).toString('hex');
const authUrl = smartClient.buildAuthUrl(state);
console.log(`เปลี่ยนเส้นทางไปที่: ${authUrl}`);

ขอบเขต SMART ที่จำเป็น

ขอบเขต (Scope) สิทธิ์ (Permission) กรณีการใช้งาน (Use Case)
openid การตรวจสอบสิทธิ์ OIDC จำเป็นสำหรับทุกแอป
profile ข้อมูลโปรไฟล์ผู้ใช้ ไดเรกทอรีผู้ให้บริการ
patient/Patient.read อ่านข้อมูลประชากรของผู้ป่วย การค้นหาผู้ป่วย
patient/Observation.read อ่านข้อมูลสังเกตการณ์ สัญญาณชีพ, ผลตรวจทางห้องปฏิบัติการ
patient/Condition.read อ่าน Condition รายการปัญหา
patient/MedicationRequest.read อ่านยา ประวัติการใช้ยา
patient/*.read อ่านทรัพยากรผู้ป่วยทั้งหมด ข้อมูลผู้ป่วยทั้งหมด
user/*.read อ่านทรัพยากรทั้งหมดที่เข้าถึงได้ มุมมองผู้ให้บริการ
offline_access รีเฟรชโทเค็น เซสชันที่ใช้งานได้นาน

การส่งคำขอ FHIR ที่ผ่านการยืนยันตัวตนแล้ว

class FHIRClient {
  constructor(accessToken, fhirBaseUrl) {
    this.accessToken = accessToken;
    this.baseUrl = fhirBaseUrl;
  }

  async request(endpoint, options = {}) {
    const response = await fetch(`${this.baseUrl}/fhir${endpoint}`, {
      ...options,
      headers: {
        'Accept': 'application/fhir+json',
        'Authorization': `Bearer ${this.accessToken}`,
        ...options.headers
      }
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`FHIR Error: ${error.issue?.[0]?.details?.text}`);
    }

    return response.json();
  }

  async getPatient(patientId) {
    return this.request(`/Patient/${patientId}`);
  }

  async searchPatients(params) {
    const query = new URLSearchParams(params);
    return this.request(`/Patient?${query.toString()}`);
  }
}

// การใช้งานหลังจาก OAuth callback
const fhirClient = new FHIRClient(tokens.accessToken, 'https://fhir.epic.com');
const patient = await fhirClient.getPatient(tokens.patientId);

การดำเนินการแบบ Batch และ Transaction

คำขอแบบ Batch

ดำเนินการคำขอหลายรายการที่เป็นอิสระจากกัน:

const batchRequest = async (requests) => {
  const bundle = {
    resourceType: 'Bundle',
    type: 'batch',
    entry: requests.map(req => ({
      resource: req.resource,
      request: {
        method: req.method,
        url: req.url
      }
    }))
  };

  const response = await fhirRequest('', {
    method: 'POST',
    body: JSON.stringify(bundle)
  });

  return response;
};

// การใช้งาน - ดึงทรัพยากรหลายรายการ
const bundle = await batchRequest([
  { method: 'GET', url: 'Patient/12345' },
  { method: 'GET', url: 'Patient/12345/Observation?category=vital-signs' },
  { method: 'GET', url: 'Patient/12345/Condition?clinical-status=active' }
]);

bundle.entry.forEach((entry, index) => {
  console.log(`การตอบกลับ ${index}: ${entry.response.status}`);
  console.log(entry.resource);
});

คำขอแบบ Transaction

ดำเนินการคำขอหลายรายการเป็นหน่วยอะตอมมิก:

const transactionRequest = async (requests) => {
  const bundle = {
    resourceType: 'Bundle',
    type: 'transaction',
    entry: requests.map(req => ({
      resource: req.resource,
      request: {
        method: req.method,
        url: req.url
      }
    }))
  };

  const response = await fhirRequest('', {
    method: 'POST',
    body: JSON.stringify(bundle)
  });

  return response;
};

// การใช้งาน - สร้างผู้ป่วยและทรัพยากรที่เกี่ยวข้อง
const transaction = await transactionRequest([
  {
    method: 'POST',
    url: 'Patient',
    resource: {
      resourceType: 'Patient',
      name: [{ family: 'Doe', given: ['Jane'] }],
      gender: 'female',
      birthDate: '1990-01-01'
    }
  },
  {
    method: 'POST',
    url: 'Condition',
    resource: {
      resourceType: 'Condition',
      clinicalStatus: { coding: [{ code: 'active' }] },
      code: { coding: [{ system: 'http://snomed.info/sct', code: '38341003' }] },
      subject: { reference: 'Patient/-1' } // อ้างอิงถึงทรัพยากรแรก
    }
  }
]);

การสมัครสมาชิกและ Webhook

การสมัครสมาชิก FHIR (R4B+)

สมัครสมาชิกเพื่อรับการเปลี่ยนแปลงทรัพยากร:

const createSubscription = async (subscriptionData) => {
  const subscription = {
    resourceType: 'Subscription',
    status: 'requested',
    criteria: subscriptionData.criteria,
    reason: subscriptionData.reason,
    channel: {
      type: 'rest-hook',
      endpoint: subscriptionData.endpoint,
      payload: 'application/fhir+json'
    }
  };

  const response = await fhirRequest('/Subscription', {
    method: 'POST',
    body: JSON.stringify(subscription)
  });

  return response;
};

// การใช้งาน - สมัครสมาชิกเพื่อรับผลการตรวจทางห้องปฏิบัติการใหม่
const subscription = await createSubscription({
  criteria: 'DiagnosticReport?category=laboratory&patient=12345',
  reason: 'ติดตามผลการตรวจทางห้องปฏิบัติการของผู้ป่วย',
  endpoint: 'https://myapp.com/webhooks/fhir'
});

การจัดการ FHIR Webhook

const express = require('express');
const app = express();

app.post('/webhooks/fhir', express.json({ type: 'application/fhir+json' }), async (req, res) => {
  const notification = req.body;

  // ตรวจสอบการอ้างอิงการสมัครสมาชิก
  if (notification.subscription !== expectedSubscription) {
    return res.status(401).send('ไม่ได้รับอนุญาต');
  }

  // ประมวลผลการแจ้งเตือน
  if (notification.event?.resourceType === 'DiagnosticReport') {
    const reportId = notification.event.resourceId;
    const report = await fhirRequest(`/DiagnosticReport/${reportId}`);

    // ประมวลผลผลการตรวจทางห้องปฏิบัติการใหม่
    await processLabResult(report);
  }

  res.status(200).send('ตกลง');
});

การแก้ไขปัญหาทั่วไป

ปัญหา: 401 Unauthorized (ไม่ได้รับอนุญาต)

อาการ: ได้รับข้อผิดพลาด “Unauthorized” หรือ “Invalid token”

วิธีแก้ปัญหา:

  1. ตรวจสอบว่าโทเค็นยังไม่หมดอายุ
  2. ตรวจสอบว่าขอบเขตโทเค็นครอบคลุมทรัพยากรที่ร้องขอ
  3. ตรวจสอบให้แน่ใจว่ามีส่วนหัว Authorization: Bearer {token}
  4. ตรวจสอบว่า URL เซิร์ฟเวอร์ FHIR ตรงกับ audience ของโทเค็น

ปัญหา: 403 Forbidden (ต้องห้าม)

อาการ: โทเค็นถูกต้อง แต่ถูกปฏิเสธการเข้าถึง

วิธีแก้ปัญหา:

  1. ตรวจสอบว่าผู้ใช้มีสิทธิ์สำหรับทรัพยากรที่ร้องขอ
  2. ตรวจสอบว่าบริบทผู้ป่วยตรงกัน (สำหรับโทเค็นที่กำหนดขอบเขตผู้ป่วย)
  3. ตรวจสอบว่า SMART scopes ครอบคลุมการดำเนินการที่ร้องขอ
  4. ตรวจสอบการควบคุมการเข้าถึงระดับทรัพยากร

ปัญหา: 404 Not Found (ไม่พบ)

อาการ: ทรัพยากรไม่มีอยู่จริง หรือ endpoint ไม่ถูกต้อง

วิธีแก้ปัญหา:

  1. ตรวจสอบว่า ID ทรัพยากรถูกต้อง
  2. ตรวจสอบว่า Base URL ของ FHIR ถูกต้อง
  3. ตรวจสอบว่าประเภททรัพยากรได้รับการสนับสนุนโดยเซิร์ฟเวอร์
  4. ตรวจสอบ endpoint เฉพาะเวอร์ชัน (R4 เทียบกับ R4B)

ปัญหา: 422 Unprocessable Entity (เอนทิตีไม่สามารถประมวลผลได้)

อาการ: ข้อผิดพลาดในการตรวจสอบความถูกต้องเมื่อสร้าง/อัปเดต

วิธีแก้ปัญหา:

// แยกวิเคราะห์ข้อผิดพลาดในการตรวจสอบความถูกต้อง
const error = await response.json();
error.issue?.forEach(issue => {
  console.log(`ความรุนแรง: ${issue.severity}`);
  console.log(`ตำแหน่ง: ${issue.expression?.join('.')}`);
  console.log(`ข้อความ: ${issue.details?.text}`);
});

สาเหตุทั่วไป:

รายการตรวจสอบสำหรับการนำไปใช้งานจริง

ก่อนนำไปใช้งานจริง:

การตรวจสอบความถูกต้องของ FHIR

const { FhirValidator } = require('fhir-validator');

const validator = new FhirValidator('4.0.1');

const validateResource = async (resource) => {
  const validationResult = await validator.validate(resource);

  if (!validationResult.valid) {
    validationResult.issues.forEach(issue => {
      console.error(`ข้อผิดพลาดในการตรวจสอบความถูกต้อง: ${issue.message}`);
      console.error(`ตำแหน่ง: ${issue.path}`);
    });
    throw new Error('การตรวจสอบความถูกต้องของทรัพยากรล้มเหลว');
  }

  return true;
};

// การใช้งานก่อนสร้าง/อัปเดต
await validateResource(patientResource);

กรณีการใช้งานจริง

การบูรณาการ Patient Portal

ระบบสุขภาพสร้าง Patient Portal:

การนำไปใช้งานหลัก:

การสนับสนุนการตัดสินใจทางคลินิก

แพลตฟอร์มการจัดการการดูแลเพิ่ม CDS:

การนำไปใช้งานหลัก:

การวิเคราะห์สุขภาพประชากร

ผู้ชำระเงินสร้างแดชบอร์ดสุขภาพประชากร:

การนำไปใช้งานหลัก:

บทสรุป

HL7 FHIR เป็นรากฐานสำหรับการทำงานร่วมกันด้านการดูแลสุขภาพในยุคปัจจุบัน ประเด็นสำคัญ:

ปุ่ม

ส่วนคำถามที่พบบ่อย

HL7 FHIR ใช้สำหรับอะไร?

FHIR ช่วยให้การแลกเปลี่ยนข้อมูลด้านการดูแลสุขภาพเป็นมาตรฐานระหว่าง EHRs, Patient Portal, แอปพลิเคชันมือถือ และระบบ IT ด้านสุขภาพอื่นๆ กรณีการใช้งานทั่วไปรวมถึงแอปพลิเคชันสำหรับผู้ป่วยในการเข้าถึงข้อมูล, การสนับสนุนการตัดสินใจทางคลินิก, สุขภาพประชากร และการประสานงานการดูแล

ฉันจะเริ่มต้นใช้งาน FHIR ได้อย่างไร?

เริ่มต้นด้วยการเข้าถึงเซิร์ฟเวอร์ FHIR สาธารณะ (เช่น HAPI FHIR test server) หรือตั้งค่าบริการ FHIR บนคลาวด์ (Azure API for FHIR, AWS HealthLake) ฝึกฝนการอ่านทรัพยากรและการใช้พารามิเตอร์การค้นหา

HL7 v2 กับ FHIR แตกต่างกันอย่างไร?

HL7 v2 ใช้ข้อความที่คั่นด้วยสัญลักษณ์ไปป์ (ADT, ORM, ORU) สำหรับการแลกเปลี่ยนข้อมูลแบบขับเคลื่อนด้วยเหตุการณ์ (event-driven) ส่วน FHIR ใช้ RESTful API กับ JSON/XML สำหรับการเข้าถึงแบบทรัพยากร (resource-based) FHIR ง่ายต่อการนำไปใช้งานและเหมาะสมกับแอปพลิเคชันเว็บ/มือถือที่ทันสมัยกว่า

FHIR ปฏิบัติตาม HIPAA หรือไม่?

FHIR เป็นมาตรฐานรูปแบบข้อมูล การปฏิบัติตาม HIPAA ขึ้นอยู่กับการนำไปใช้งาน: การเข้ารหัส, การยืนยันตัวตน, การควบคุมการเข้าถึง และการบันทึกการตรวจสอบ ใช้ OAuth 2.0 กับ SMART on FHIR เพื่อการเข้าถึงที่ปลอดภัย

SMART scopes คืออะไร?

SMART scopes กำหนดสิทธิ์การเข้าถึงแบบละเอียดสำหรับทรัพยากร FHIR (เช่น patient/Observation.read, user/*.read) ร้องขอเฉพาะ scopes ที่แอปของคุณต้องการเท่านั้น

ฉันจะค้นหาทรัพยากรใน FHIR ได้อย่างไร?

ใช้คำขอ GET พร้อมพารามิเตอร์การค้นหา: /Patient?name=Smith&birthdate=ge1980-01-01 FHIR รองรับตัวปรับแต่ง (:exact, :contains) และคำนำหน้า (gt, lt, ge, le)

Bulk FHIR คืออะไร?

Bulk FHIR ($export) ช่วยให้สามารถส่งออกชุดข้อมูลขนาดใหญ่ในรูปแบบ NDJSON แบบไม่พร้อมกันได้ ใช้สำหรับสุขภาพประชากร, การวิเคราะห์ และการจัดเก็บข้อมูล

ฉันจะจัดการการกำหนดเวอร์ชันของ FHIR ได้อย่างไร?

กำหนดเป้าหมายเวอร์ชัน FHIR ที่เฉพาะเจาะจง (แนะนำ R4) และใช้ endpoint เฉพาะเวอร์ชัน ตรวจสอบ CapabilityStatement สำหรับเวอร์ชันและทรัพยากรที่รองรับ

ฉันสามารถขยาย FHIR ด้วยฟิลด์ที่กำหนดเองได้หรือไม่?

ได้ คุณสามารถใช้ส่วนขยาย FHIR (FHIR extensions) เพื่อเพิ่มองค์ประกอบข้อมูลที่กำหนดเอง กำหนดส่วนขยายใน Implementation Guide ของคุณและลงทะเบียนกับ HL7 หากต้องการแบ่งปันอย่างกว้างขวาง

มีเครื่องมือใดบ้างที่ช่วยในการพัฒนา FHIR?

เครื่องมือยอดนิยมได้แก่ HAPI FHIR (เซิร์ฟเวอร์โอเพนซอร์ส), FHIR validator, Postman collections และ Apidog สำหรับการทดสอบและจัดทำเอกสาร API

ฝึกการออกแบบ API แบบ Design-first ใน Apidog

ค้นพบวิธีที่ง่ายขึ้นในการสร้างและใช้ API