DEV Community

Cover image for Cómo Usar la API HL7 FHIR: Guía Completa de Integración en Salud (2026)
Roobia
Roobia

Posted on • Originally published at apidog.com

Cómo Usar la API HL7 FHIR: Guía Completa de Integración en Salud (2026)

Resumen

HL7 FHIR (Fast Healthcare Interoperability Resources) es el estándar moderno para el intercambio de datos de atención médica, utilizando API RESTful con respuestas JSON/XML. Proporciona recursos estandarizados para pacientes, observaciones, medicamentos y más, con autenticación OAuth 2.0 y SMART on FHIR para la integración de aplicaciones. Esta guía cubre la arquitectura de FHIR, los tipos de recursos, los parámetros de búsqueda, la autenticación y las estrategias de implementación en producción.

Prueba Apidog hoy

Introducción

La fragmentación de los datos de atención médica le cuesta al sistema de salud de EE. UU. 30 mil millones de dólares anuales. Para los desarrolladores que crean aplicaciones de atención médica, la integración de la API HL7 FHIR no es opcional, es el estándar de la industria exigido por CMS y adoptado por Epic, Cerner y todos los principales proveedores de EHR.

Esta es la realidad: los proveedores que utilizan aplicaciones habilitadas para FHIR reducen el tiempo de coordinación de la atención en un 40% y eliminan el 85% de las solicitudes de registros basadas en fax. Una sólida integración de la API de FHIR permite un intercambio de datos fluido entre los EHR, los portales de pacientes y las plataformas de coordinación de la atención.

Esta guía le guiará a través del proceso completo de integración de la API HL7 FHIR. Aprenderá la arquitectura de FHIR, los tipos de recursos, los parámetros de búsqueda, la autenticación OAuth 2.0, la integración de SMART on FHIR y las estrategias de implementación en producción. Al final, tendrá una integración de FHIR lista para producción.

💡 Apidog simplifica la integración de la API de atención médica. Pruebe los puntos finales de FHIR, valide los esquemas de recursos, depure los flujos de autenticación y documente las especificaciones de la API en un solo espacio de trabajo. Importe las Guías de implementación de FHIR, simule respuestas y comparta escenarios de prueba con su equipo.

¿Qué es HL7 FHIR?

FHIR (Fast Healthcare Interoperability Resources) es un marco de estándares para el intercambio electrónico de información de atención médica. Desarrollado por Health Level Seven International (HL7), FHIR utiliza tecnologías web modernas que incluyen API RESTful, JSON, XML y OAuth 2.0.

Arquitectura FHIR

Tipos de recursos FHIR

FHIR define más de 140 tipos de recursos. Los recursos principales incluyen:

Recurso Propósito Casos de uso comunes
Paciente Datos demográficos Búsqueda de pacientes, registro
Profesional Información del proveedor Directorio, programación
Encuentro Visitas/ingresos Episodios de atención, facturación
Observación Datos clínicos Signos vitales, resultados de laboratorio, evaluaciones
Condición Problemas/diagnósticos Listas de problemas, planificación de la atención
SolicitudDeMedicamento Recetas Receta electrónica, historial de medicación
AlergiaIntolerancia Alergias Verificaciones de seguridad, alertas
Inmunización Vacunaciones Registros de inmunización
InformeDiagnóstico Informes de laboratorio/imágenes Entrega de resultados
ReferenciaDocumento Documentos clínicos CCD, resúmenes de alta

Arquitectura de la API de FHIR

FHIR utiliza una estructura de API RESTful:

https://fhir-server.com/fhir/{resourceType}/{id}
Enter fullscreen mode Exit fullscreen mode

Versiones de FHIR comparadas

Versión Estado Caso de uso
R4 (4.0.1) STU actual Implementaciones en producción
R4B (4.3) Implementación de prueba Adoptadores tempranos
R5 (5.0.0) Borrador STU Implementaciones futuras
DSTU2 Obsoleto Solo sistemas heredados

CMS exige que los EHR certificados admitan FHIR R4 para las API de acceso a pacientes y acceso a proveedores.

Primeros pasos: Acceso al servidor FHIR

Paso 1: Elija su servidor FHIR

Opciones para el despliegue del servidor FHIR:

Servidor Tipo Costo Ideal para
API de Azure para FHIR Administrado Pago por uso Empresas, entornos Azure
AWS HealthLake Administrado Pago por uso Entornos AWS
API de Google Cloud Healthcare Administrado Pago por uso Entornos GCP
HAPI FHIR Código abierto Autoalojado Despliegues personalizados
Servidor FHIR de Epic Comercial Clientes de Epic Integración con EHR de Epic
Cerner Ignite FHIR Comercial Clientes de Cerner Integración con EHR de Cerner

Paso 2: Obtener credenciales del servidor

Para servicios FHIR en la nube:

# API de Azure para FHIR
# 1. Cree el Servicio FHIR en Azure Portal
# 2. Configure la autenticación (OAuth 2.0 o AAD)
# 3. Obtenga el endpoint de FHIR: https://{service-name}.azurehealthcareapis.com
# 4. Registre la aplicación cliente para OAuth

# AWS HealthLake
# 1. Cree un Almacén de Datos en la Consola de AWS
# 2. Configure los roles de IAM
# 3. Obtenga el endpoint: https://healthlake.{region}.amazonaws.com
Enter fullscreen mode Exit fullscreen mode

Paso 3: Entender las operaciones RESTful de FHIR

FHIR soporta métodos HTTP estándar:

Operación Método HTTP Endpoint Descripción
Lectura GET /{resourceType}/{id} Obtener recurso específico
Búsqueda GET /{resourceType}?param=value Buscar recursos
Crear POST /{resourceType} Crear nuevo recurso
Actualizar PUT /{resourceType}/{id} Reemplazar recurso
Parchear PATCH /{resourceType}/{id} Actualización parcial
Eliminar DELETE /{resourceType}/{id} Eliminar recurso
Historial GET /{resourceType}/{id}/_history Versiones de recursos

Paso 4: Realizar su primera llamada FHIR

Probar conectividad:

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

Respuesta esperada:

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

Operaciones FHIR principales

Lectura de un recurso de paciente

Obtener paciente por 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();
};

// Leer paciente por ID
const getPatient = async (patientId) => {
  const patient = await fhirRequest(`/Patient/${patientId}`);
  return patient;
};

// Uso
const patient = await getPatient('12345');
console.log(`Paciente: ${patient.name[0].given[0]} ${patient.name[0].family}`);
console.log(`Fecha de nacimiento: ${patient.birthDate}`);
console.log(`Género: ${patient.gender}`);
Enter fullscreen mode Exit fullscreen mode

Estructura del recurso Paciente

{
  "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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Búsqueda de recursos

Buscar pacientes por nombre:

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

  // Añadir parámetros de búsqueda
  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;
};

// Uso
const results = await searchPatients({ name: 'Smith', birthDate: '1985-06-15' });

console.log(`Se encontraron ${results.total} pacientes`);
results.entry.forEach(entry => {
  const patient = entry.resource;
  console.log(`${patient.name[0].family}, ${patient.name[0].given[0]}`);
});
Enter fullscreen mode Exit fullscreen mode

Parámetros de búsqueda comunes

Recurso Parámetros de búsqueda Ejemplo
Paciente name, birthdate, identifier, gender, phone, email ?name=Smith&birthdate=1985-06-15
Observación patient, code, date, category, status ?patient=123&code=8480-6&date=ge2026-01-01
Condición patient, clinical-status, category, onset-date ?patient=123&clinical-status=active
SolicitudDeMedicamento patient, status, intent, medication ?patient=123&status=active
Encuentro patient, date, status, class ?patient=123&date=ge2026-01-01
InformeDiagnóstico patient, category, date, status ?patient=123&category=laboratory

Modificadores de búsqueda

Modificador Descripción Ejemplo
:exact Coincidencia exacta name:exact=Smith
:contains Contiene name:contains=smi
:missing Valor presente/ausente phone:missing=true
: (prefix) Operadores de prefijo birthdate=ge1980-01-01

Prefijos de búsqueda para fechas y números

Prefijo Significado Ejemplo
eq Igual a birthdate=eq1985-06-15
ne No igual a birthdate=ne1985-06-15
gt Mayor que birthdate=gt1980-01-01
lt Menor que birthdate=lt1990-01-01
ge Mayor o igual birthdate=ge1980-01-01
le Menor o igual birthdate=le1990-01-01
sa Comienza después de date=sa2026-01-01
eb Termina antes de date=eb2026-12-31

Trabajando con datos clínicos

Creación de una observación (Signos vitales)

Registrar signos vitales:

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,
        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;
};

// Uso - Registrar presión arterial
const systolicBP = await createObservation({
  patientId: '12345',
  code: '8480-6',
  display: 'Systolic blood pressure',
  value: 120,
  unit: 'mmHg',
  ucumCode: 'mm[Hg]',
  performerId: '67890'
});

console.log(`Observación creada: ${systolicBP.id}`);
Enter fullscreen mode Exit fullscreen mode

Códigos LOINC comunes

Código Visualización Categoría
8480-6 Presión arterial sistólica Signos vitales
8462-4 Presión arterial diastólica Signos vitales
8867-4 Frecuencia cardíaca Signos vitales
8310-5 Temperatura corporal Signos vitales
8302-2 Altura corporal Signos vitales
29463-7 Peso corporal Signos vitales
8871-5 Frecuencia respiratoria Signos vitales
2339-0 Glucosa [Masa/volumen] Laboratorio
4548-4 Hemoglobina A1c Laboratorio
2093-3 Colesterol [Masa/volumen] Laboratorio

Creación de una condición (Entrada en la lista de problemas)

Añadir diagnóstico a la lista de problemas:

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;
};

// Uso - Añadir diabetes a la lista de problemas
const diabetes = await createCondition({
  patientId: '12345',
  sctCode: '44054006',
  display: 'Type 2 Diabetes Mellitus',
  status: 'active',
  category: 'problem-list-item',
  onsetDate: '2024-01-15'
});
Enter fullscreen mode Exit fullscreen mode

Códigos SNOMED CT comunes

Código Visualización Categoría
44054006 Diabetes Mellitus tipo 2 Problema
38341003 Hipertensión Problema
195967001 Asma Problema
13645005 Enfermedad Pulmonar Obstructiva Crónica Problema
35489007 Trastorno Depresivo Problema
22298006 Infarto de Miocardio Problema
26929004 Enfermedad de Alzheimer Problema
396275006 Osteoartritis Problema

Recuperación de medicamentos del paciente

Obtener solicitudes de medicamentos activas:

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

  return response;
};

// Uso
const medications = await getPatientMedications('12345');

medications.entry?.forEach(entry => {
  const med = entry.resource;
  console.log(`${med.medicationCodeableConcept.coding[0].display}`);
  console.log(`  Dosis: ${med.dosageInstruction[0]?.text}`);
  console.log(`  Estado: ${med.status}`);
});
Enter fullscreen mode Exit fullscreen mode

Recuperación de resultados de laboratorio

Obtener informes de diagnóstico y observaciones:

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;
};

// Obtener prueba de laboratorio específica (p. ej., HbA1c)
const getLabValue = async (patientId, loincCode) => {
  const params = new URLSearchParams({
    patient: patientId,
    code: loincCode
  });

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

// Uso - Obtener resultados de 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(`Fecha: ${obs.effectiveDateTime}`);
});
Enter fullscreen mode Exit fullscreen mode

OAuth 2.0 y SMART on FHIR

Comprendiendo la autenticación FHIR

Los servidores FHIR utilizan OAuth 2.0 con OpenID Connect:

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Cliente   │───▶│   Servidor  │───▶│   Servidor  │
│   (App)     │    │   de Autent.│    │   FHIR      │
└─────────────┘    └─────────────┘    └─────────────┘
     │                    │                    │
     │  1. Solicitud de   │                    │
     │     autenticación  │                    │
     │───────────────────▶│                    │
     │                    │                    │
     │  2. Inicio de      │                    │
     │     sesión usuario │                    │
     │◀───────────────────│                    │
     │                    │                    │
     │  3. Código de      │                    │
     │     autenticación  │                    │
     │───────────────────▶│                    │
     │                    │                    │
     │  4. Solicitud de   │                    │
     │     token          │                    │
     │───────────────────▶│                    │
     │                    │  5. Token + ID     │
     │◀───────────────────│                    │
     │                    │                    │
     │  6. Solicitud de   │                    │
     │     API            │                    │
     │────────────────────────────────────────▶│
     │                    │                    │
     │  7. Datos FHIR     │                    │
     │◀────────────────────────────────────────│
Enter fullscreen mode Exit fullscreen mode

Inicio de aplicación SMART on FHIR

Implementar el inicio de aplicación SMART:

const crypto = require('crypto');

class SMARTClient {
  constructor(config) {
    this.clientId = config.clientId;
    this.redirectUri = config.redirectUri;
    this.issuer = config.issuer; // URL del servidor 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();

    // Almacenar codeVerifier para el intercambio de tokens
    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
    };
  }
}

// Uso
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'
  ]
});

// Redirigir al usuario a la URL de autenticación
const state = crypto.randomBytes(16).toString('hex');
const authUrl = smartClient.buildAuthUrl(state);
console.log(`Redirigir a: ${authUrl}`);
Enter fullscreen mode Exit fullscreen mode

Ámbitos SMART requeridos

Ámbito Permiso Caso de uso
openid Autenticación OIDC Requerido para todas las aplicaciones
profile Información de perfil de usuario Directorio de proveedores
patient/Patient.read Leer datos demográficos del paciente Búsqueda de pacientes
patient/Observation.read Leer observaciones Signos vitales, laboratorios
patient/Condition.read Leer condiciones Listas de problemas
patient/MedicationRequest.read Leer medicamentos Historial de medicamentos
patient/*.read Leer todos los recursos del paciente Datos completos del paciente
user/*.read Leer todos los recursos accesibles Vista del proveedor
offline_access Token de actualización Sesiones de larga duración

Realizando solicitudes FHIR autenticadas

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()}`);
  }
}

// Uso después de la devolución de llamada de OAuth
const fhirClient = new FHIRClient(tokens.accessToken, 'https://fhir.epic.com');
const patient = await fhirClient.getPatient(tokens.patientId);
Enter fullscreen mode Exit fullscreen mode

Operaciones por lotes y transacciones

Solicitudes por lotes

Ejecutar múltiples solicitudes independientes:

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;
};

// Uso - Obtener múltiples recursos
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(`Respuesta ${index}: ${entry.response.status}`);
  console.log(entry.resource);
});
Enter fullscreen mode Exit fullscreen mode

Solicitudes de transacción

Ejecutar múltiples solicitudes como una unidad atómica:

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;
};

// Uso - Crear paciente y recursos relacionados
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' } // Referencia al primer recurso
    }
  }
]);
Enter fullscreen mode Exit fullscreen mode

Suscripciones y Webhooks

Suscripciones FHIR (R4B+)

Suscribirse a cambios de recursos:

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;
};

// Uso - Suscribirse a nuevos resultados de laboratorio
const subscription = await createSubscription({
  criteria: 'DiagnosticReport?category=laboratory&patient=12345',
  reason: 'Monitor patient lab results',
  endpoint: 'https://myapp.com/webhooks/fhir'
});
Enter fullscreen mode Exit fullscreen mode

Manejo de Webhooks FHIR

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

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

  // Verificar referencia de suscripción
  if (notification.subscription !== expectedSubscription) {
    return res.status(401).send('Unauthorized');
  }

  // Procesar notificación
  if (notification.event?.resourceType === 'DiagnosticReport') {
    const reportId = notification.event.resourceId;
    const report = await fhirRequest(`/DiagnosticReport/${reportId}`);

    // Procesar nuevo resultado de laboratorio
    await processLabResult(report);
  }

  res.status(200).send('OK');
});
Enter fullscreen mode Exit fullscreen mode

Resolución de problemas comunes

Problema: 401 No autorizado

Síntomas: Recibir errores de “No autorizado” o “Token inválido”.

Soluciones:

  1. Verificar que el token no haya expirado
  2. Comprobar que el ámbito del token incluye el recurso solicitado
  3. Asegurarse de que el encabezado Authorization: Bearer {token} esté presente
  4. Verificar que la URL del servidor FHIR coincide con la audiencia del token

Problema: 403 Prohibido

Síntomas: El token es válido pero el acceso es denegado.

Soluciones:

  1. Comprobar que el usuario tiene permiso para el recurso solicitado
  2. Verificar que el contexto del paciente coincide (para tokens con ámbito de paciente)
  3. Asegurarse de que los ámbitos SMART incluyen la operación solicitada
  4. Revisar los controles de acceso a nivel de recurso

Problema: 404 No encontrado

Síntomas: El recurso no existe o el endpoint es incorrecto.

Soluciones:

  1. Verificar que el ID del recurso es correcto
  2. Comprobar que la URL base de FHIR es correcta
  3. Asegurarse de que el tipo de recurso es soportado por el servidor
  4. Verificar el endpoint específico de la versión (R4 vs R4B)

Problema: 422 Entidad no procesable

Síntomas: Errores de validación al crear/actualizar.

Soluciones:

// Analizar errores de validación
const error = await response.json();
error.issue?.forEach(issue => {
  console.log(`Gravedad: ${issue.severity}`);
  console.log(`Ubicación: ${issue.expression?.join('.')}`);
  console.log(`Mensaje: ${issue.details?.text}`);
});
Enter fullscreen mode Exit fullscreen mode

Causas comunes:

  • Campos obligatorios faltantes
  • Valores de sistema de código inválidos
  • Formato de referencia incorrecto
  • Problemas de formato de fecha

Lista de verificación para la implementación en producción

Antes de salir a producción:

  • [ ] Configurar OAuth 2.0 con SMART on FHIR
  • [ ] Implementar lógica de refresco de tokens
  • [ ] Configurar un manejo de errores adecuado
  • [ ] Añadir registro completo (sin PHI en los logs)
  • [ ] Implementar limitación de tasa
  • [ ] Configurar lógica de reintentos con retroceso exponencial
  • [ ] Probar con múltiples proveedores de EHR
  • [ ] Validar contra el validador FHIR
  • [ ] Documentar todas las operaciones FHIR
  • [ ] Configurar monitoreo y alertas
  • [ ] Crear un manual de operaciones para problemas comunes

Validación 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(`Error de validación: ${issue.message}`);
      console.error(`Ubicación: ${issue.path}`);
    });
    throw new Error('Resource validation failed');
  }

  return true;
};

// Uso antes de crear/actualizar
await validateResource(patientResource);
Enter fullscreen mode Exit fullscreen mode

Casos de uso en el mundo real

Integración del portal de pacientes

Un sistema de salud construye un portal de pacientes:

  • Desafío: Los pacientes no podían acceder a los registros de múltiples proveedores
  • Solución: Aplicación SMART on FHIR con integración de Epic y Cerner
  • Resultado: 80% de adopción por parte de los pacientes, 50% de reducción en las solicitudes de registros

Implementación clave:

  • Lanzamiento de la aplicación SMART orientada al paciente
  • Acceso de solo lectura a Paciente, Observación, Condición, SolicitudDeMedicamento
  • Token de actualización para sesiones persistentes
  • Interfaz de usuario adaptable a móviles

Soporte a la decisión clínica

Una plataforma de gestión de la atención añade CDS:

  • Desafío: Los proveedores perdían oportunidades de atención preventiva
  • Solución: Consultas FHIR en tiempo real para detectar brechas en la atención
  • Resultado: 25% de mejora en las puntuaciones HEDIS

Implementación clave:

  • Aplicación SMART orientada al proveedor
  • Consultar Paciente, Condición, Observación, Inmunización
  • Calcular brechas en la atención basándose en guías
  • Recomendaciones en línea en el flujo de trabajo del EHR

Análisis de salud poblacional

Un pagador construye un panel de salud poblacional:

  • Desafío: Datos incompletos en las redes de proveedores
  • Solución: Exportación masiva de datos FHIR para análisis
  • Resultado: Vista de 360 grados del paciente, costos PMPM reducidos

Implementación clave:

  • Acceso masivo a datos FHIR ($export)
  • Exportaciones nocturnas a almacén de datos
  • Modelos de estratificación de riesgos
  • Alertas para el gestor de casos

Conclusión

HL7 FHIR proporciona la base para la interoperabilidad moderna en la atención médica. Puntos clave:

  • FHIR R4 es el estándar actual para la integración de API de atención médica
  • SMART on FHIR permite una autenticación segura con OAuth 2.0
  • Los tipos de recursos estandarizan los datos de pacientes, observaciones, condiciones y medicamentos
  • Los parámetros de búsqueda permiten consultas flexibles
  • Las operaciones por lotes y transacciones soportan flujos de trabajo complejos
  • Apidog agiliza las pruebas y la documentación de la API de FHIR

Sección de Preguntas Frecuentes

¿Para qué se utiliza HL7 FHIR?

FHIR permite el intercambio estandarizado de datos de atención médica entre EHR, portales de pacientes, aplicaciones móviles y otros sistemas de TI de salud. Los casos de uso comunes incluyen aplicaciones de acceso para pacientes, soporte a la decisión clínica, salud poblacional y coordinación de la atención.

¿Cómo puedo empezar con FHIR?

Comience accediendo a un servidor FHIR público (como el servidor de prueba HAPI FHIR) o configure un servicio FHIR en la nube (API de Azure para FHIR, AWS HealthLake). Practique la lectura de recursos y el uso de parámetros de búsqueda.

¿Cuál es la diferencia entre HL7 v2 y FHIR?

HL7 v2 utiliza mensajes delimitados por tuberías (ADT, ORM, ORU) para el intercambio de datos basado en eventos. FHIR utiliza API RESTful con JSON/XML para el acceso basado en recursos. FHIR es más fácil de implementar y más adecuado para aplicaciones web/móviles modernas.

¿Es FHIR compatible con HIPAA?

FHIR en sí mismo es un estándar de formato de datos. La compatibilidad con HIPAA depende de la implementación: cifrado, autenticación, controles de acceso y registro de auditoría. Utilice OAuth 2.0 con SMART on FHIR para un acceso seguro.

¿Qué son los ámbitos SMART?

Los ámbitos SMART definen permisos de acceso granulares para los recursos FHIR (por ejemplo, patient/Observation.read, user/*.read). Solicite solo los ámbitos que su aplicación necesite.

¿Cómo busco recursos en FHIR?

Utilice solicitudes GET con parámetros de consulta: /Patient?name=Smith&birthdate=ge1980-01-01. FHIR soporta modificadores (:exact, :contains) y prefijos (gt, lt, ge, le).

¿Qué es FHIR masivo?

FHIR masivo ($export) permite la exportación asíncrona de grandes conjuntos de datos en formato NDJSON. Se utiliza para la salud poblacional, análisis y almacenamiento de datos.

¿Cómo manejo el versionado de FHIR?

Diríjase a una versión específica de FHIR (se recomienda R4) y utilice endpoints específicos de la versión. Consulte el CapabilityStatement para ver las versiones y recursos compatibles.

¿Puedo extender FHIR con campos personalizados?

Sí, utilice las extensiones de FHIR para añadir elementos de datos personalizados. Defina las extensiones en su Guía de Implementación y regístrelas con HL7 si las va a compartir ampliamente.

¿Qué herramientas ayudan con el desarrollo de FHIR?

Las herramientas populares incluyen HAPI FHIR (servidor de código abierto), validador FHIR, colecciones de Postman y Apidog para pruebas y documentación de API.

Top comments (0)