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.
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.
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}
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
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}"
Respuesta esperada:
{
"resourceType": "CapabilityStatement",
"status": "active",
"date": "2026-03-25",
"fhirVersion": "4.0.1",
"rest": [{
"mode": "server",
"resource": [
{ "type": "Patient" },
{ "type": "Observation" },
{ "type": "Condition" }
]
}]
}
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}`);
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"
}
]
}
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]}`);
});
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}`);
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'
});
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}`);
});
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}`);
});
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 │ │
│◀────────────────────────────────────────│
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}`);
Á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);
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);
});
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
}
}
]);
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'
});
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');
});
Resolución de problemas comunes
Problema: 401 No autorizado
Síntomas: Recibir errores de “No autorizado” o “Token inválido”.
Soluciones:
- Verificar que el token no haya expirado
- Comprobar que el ámbito del token incluye el recurso solicitado
- Asegurarse de que el encabezado
Authorization: Bearer {token}esté presente - 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:
- Comprobar que el usuario tiene permiso para el recurso solicitado
- Verificar que el contexto del paciente coincide (para tokens con ámbito de paciente)
- Asegurarse de que los ámbitos SMART incluyen la operación solicitada
- 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:
- Verificar que el ID del recurso es correcto
- Comprobar que la URL base de FHIR es correcta
- Asegurarse de que el tipo de recurso es soportado por el servidor
- 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}`);
});
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);
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)