DEV Community

Cover image for Como Usar a API HL7 FHIR: Guia Completo de Integração em Saúde (2026)
Lucas
Lucas

Posted on • Originally published at apidog.com

Como Usar a API HL7 FHIR: Guia Completo de Integração em Saúde (2026)

TL;DR

HL7 FHIR (Fast Healthcare Interoperability Resources) é o padrão moderno para troca de dados de saúde, usando APIs RESTful com respostas JSON/XML. Ele fornece recursos padronizados para pacientes, observações, medicamentos e muito mais, com autenticação OAuth 2.0 e SMART on FHIR para integração de aplicativos. Este guia abrange a arquitetura FHIR, tipos de recursos, parâmetros de busca, autenticação e estratégias de implementação em produção.

Experimente o Apidog hoje

💡 Dica: Apidog simplifica a integração de APIs de saúde. Teste endpoints FHIR, valide esquemas de recursos, depure fluxos de autenticação e documente especificações de API em um único espaço de trabalho. Importe Guias de Implementação FHIR, simule respostas e compartilhe cenários de teste com sua equipe.

O Que É HL7 FHIR?

FHIR (Fast Healthcare Interoperability Resources) é uma estrutura de padrões para troca eletrônica de informações de saúde. Desenvolvido pela Health Level Seven International (HL7), o FHIR utiliza APIs RESTful, JSON, XML e OAuth 2.0.

Tipos de recursos FHIR mais usados

Tipos de Recursos FHIR

FHIR define mais de 140 tipos de recursos. Os principais para implementar:

Recurso Propósito Casos de Uso Comuns
Patient (Paciente) Demográficos Consulta de pacientes, registro
Practitioner (Profissional) Informações do provedor Diretório, agendamento
Encounter (Atendimento) Consultas/internações Episódios de cuidado, faturamento
Observation (Observação) Dados clínicos Sinais vitais, resultados de laboratório, avaliações
Condition (Condição) Problemas/diagnósticos Listas de problemas, plano de cuidados
MedicationRequest (Solicitação de Medicação) Prescrições Prescrição eletrônica, histórico de medicação
AllergyIntolerance (AlergiaIntolerância) Alergias Verificações de segurança, alertas
Immunization (Imunização) Vacinações Registros de imunização
DiagnosticReport (Relatório Diagnóstico) Relatórios de laboratório/imagem Entrega de resultados
DocumentReference (Referência de Documento) Documentos clínicos CCD, sumários de alta

Arquitetura da API FHIR

A estrutura básica do endpoint RESTful é:

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

Versões FHIR Comparadas

Versão Status Caso de Uso
R4 (4.0.1) STU Atual Implementações em produção
R4B (4.3) Implementação de Teste Adotantes iniciais
R5 (5.0.0) Rascunho STU Futuras implementações
DSTU2 Obsoleto Apenas sistemas legados

O CMS exige suporte ao FHIR R4 em APIs de acesso ao paciente e provedor.


Primeiros Passos: Acesso ao Servidor FHIR

Passo 1: Escolha Seu Servidor FHIR

Opções de servidores para ambientes reais e testes:

Servidor Tipo Custo Melhor Para
Azure API for FHIR Gerenciado Pagamento por uso Empresas, usuários Azure
AWS HealthLake Gerenciado Pagamento por uso Ambientes AWS
Google Cloud Healthcare API Gerenciado Pagamento por uso Ambientes GCP
HAPI FHIR Código Aberto Auto-hospedado Implantações personalizadas
Epic FHIR Server Comercial Clientes Epic Integração com EHR Epic
Cerner Ignite FHIR Comercial Clientes Cerner Integração com EHR Cerner

Passo 2: Obtenha as Credenciais do Servidor

Exemplo para cloud:

# Azure API for FHIR
# 1. Crie o Serviço FHIR no Portal Azure
# 2. Configure a autenticação (OAuth 2.0 ou AAD)
# 3. Obtenha o endpoint FHIR: https://{nome-do-serviço}.azurehealthcareapis.com
# 4. Registre o aplicativo cliente para OAuth

# AWS HealthLake
# 1. Crie o Data Store no Console AWS
# 2. Configure as funções IAM
# 3. Obtenha o endpoint: https://healthlake.{região}.amazonaws.com
Enter fullscreen mode Exit fullscreen mode

Passo 3: Entenda as Operações RESTful do FHIR

Operação Método HTTP Endpoint Descrição
Read (Ler) GET /{resourceType}/{id} Obter recurso específico
Search (Buscar) GET /{resourceType}?param=value Buscar recursos
Create (Criar) POST /{resourceType} Criar novo recurso
Update (Atualizar) PUT /{resourceType}/{id} Substituir recurso
Patch (Aplicar Patch) PATCH /{resourceType}/{id} Atualização parcial
Delete (Excluir) DELETE /{resourceType}/{id} Remover recurso
History (Histórico) GET /{resourceType}/{id}/_history Versões do recurso

Passo 4: Faça Sua Primeira Chamada FHIR

Teste a conectividade:

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

Resposta 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

Operações FHIR Essenciais

Lendo um Recurso de Paciente

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

// Ler 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(`Data de Nascimento: ${patient.birthDate}`);
console.log(`Gênero: ${patient.gender}`);
Enter fullscreen mode Exit fullscreen mode

Estrutura do Recurso de 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

Buscando Recursos

Busque pacientes por nome:

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

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

console.log(`Encontrados ${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 Busca Comuns

Recurso Parâmetros de Busca Exemplo
Patient (Paciente) name, birthdate, identifier, gender, phone, email ?name=Smith&birthdate=1985-06-15
Observation (Observação) patient, code, date, category, status ?patient=123&code=8480-6&date=ge2026-01-01
Condition (Condição) patient, clinical-status, category, onset-date ?patient=123&clinical-status=active
MedicationRequest (Solicitação de Medicação) patient, status, intent, medication ?patient=123&status=active
Encounter (Atendimento) patient, date, status, class ?patient=123&date=ge2026-01-01
DiagnosticReport (Relatório Diagnóstico) patient, category, date, status ?patient=123&category=laboratory

Modificadores de Busca

Modificador Descrição Exemplo
:exact Correspondência exata name:exact=Smith
:contains Contém name:contains=smi
:missing Valor presente/ausente phone:missing=true
: (prefixo) Operadores de prefixo birthdate=ge1980-01-01

Prefixos de Busca para Datas e Números

Prefixo Significado Exemplo
eq Igual a birthdate=eq1985-06-15
ne Diferente de birthdate=ne1985-06-15
gt Maior que birthdate=gt1980-01-01
lt Menor que birthdate=lt1990-01-01
ge Maior ou igual a birthdate=ge1980-01-01
le Menor ou igual a birthdate=le1990-01-01
sa Começa depois de date=sa2026-01-01
eb Termina antes de date=eb2026-12-31

Trabalhando com Dados Clínicos

Criando uma Observação (Sinais Vitais)

Registrar sinais vitais:

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 pressão arterial
const systolicBP = await createObservation({
  patientId: '12345',
  code: '8480-6',
  display: 'Pressão arterial sistólica',
  value: 120,
  unit: 'mmHg',
  ucumCode: 'mm[Hg]',
  performerId: '67890'
});

console.log(`Observação criada: ${systolicBP.id}`);
Enter fullscreen mode Exit fullscreen mode

Códigos LOINC Comuns

Código Exibição Categoria
8480-6 Pressão arterial sistólica Sinais vitais
8462-4 Pressão arterial diastólica Sinais vitais
8867-4 Frequência cardíaca Sinais vitais
8310-5 Temperatura corporal Sinais vitais
8302-2 Altura corporal Sinais vitais
29463-7 Peso corporal Sinais vitais
8871-5 Frequência respiratória Sinais vitais
2339-0 Glicose [Massa/volume] Laboratório
4548-4 Hemoglobina A1c Laboratório
2093-3 Colesterol [Massa/volume] Laboratório

Criando uma Condição (Entrada na Lista de Problemas)

Adicionar diagnóstico:

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 - Adicionar diabetes à lista de problemas
const diabetes = await createCondition({
  patientId: '12345',
  sctCode: '44054006',
  display: 'Diabetes Mellitus Tipo 2',
  status: 'active',
  category: 'problem-list-item',
  onsetDate: '2024-01-15'
});
Enter fullscreen mode Exit fullscreen mode

Códigos SNOMED CT Comuns

Código Exibição Categoria
44054006 Diabetes Mellitus Tipo 2 Problema
38341003 Hipertensão Problema
195967001 Asma Problema
13645005 Doença Pulmonar Obstrutiva Crônica Problema
35489007 Transtorno Depressivo Problema
22298006 Infarto do Miocárdio Problema
26929004 Doença de Alzheimer Problema
396275006 Osteoartrite Problema

Recuperando Medicações do Paciente

Obtenha solicitações de medicação ativas:

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(`  Dose: ${med.dosageInstruction[0]?.text}`);
  console.log(`  Status: ${med.status}`);
});
Enter fullscreen mode Exit fullscreen mode

Recuperando Resultados de Laboratório

Obtenha relatórios diagnósticos e observações:

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

// Obter teste de laboratório específico (ex.: HbA1c)
const getLabValue = async (patientId, loincCode) => {
  const params = new URLSearchParams({
    patient: patientId,
    code: loincCode
  });

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

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

OAuth 2.0 e SMART on FHIR

Entendendo a Autenticação FHIR

Fluxo típico OAuth 2.0/OpenID Connect:

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Cliente   │───▶│   Servidor  │───▶│   Servidor  │
│   (App)     │    │   de Auth   │    │   FHIR      │
└─────────────┘    └─────────────┘    └─────────────┘
     │                    │                    │
     │  1. Solicitação    │                    │
     │     de Auth        │                    │
     │───────────────────▶│                    │
     │                    │                    │
     │  2. Login do       │                    │
     │     Usuário        │                    │
     │◀───────────────────│                    │
     │                    │                    │
     │  3. Código         │                    │
     │     de Auth        │                    │
     │───────────────────▶│                    │
     │                    │                    │
     │  4. Solicitação    │                    │
     │     de Token       │                    │
     │───────────────────▶│                    │
     │                    │  5. Token + ID     │
     │◀───────────────────│                    │
     │                    │                    │
     │  6. Solicitação    │                    │
     │     de API         │                    │
     │────────────────────────────────────────▶│
     │                    │                    │
     │  7. Dados FHIR     │                    │
     │◀────────────────────────────────────────│
Enter fullscreen mode Exit fullscreen mode

Lançamento de Aplicativo SMART on FHIR

Implementação básica em Node.js:

const crypto = require('crypto');

class SMARTClient {
  constructor(config) {
    this.clientId = config.clientId;
    this.redirectUri = config.redirectUri;
    this.issuer = config.issuer;
    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();
    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'
  ]
});

// Redirecionar usuário para a URL de autenticação
const state = crypto.randomBytes(16).toString('hex');
const authUrl = smartClient.buildAuthUrl(state);
console.log(`Redirecionar para: ${authUrl}`);
Enter fullscreen mode Exit fullscreen mode

Escopos SMART Necessários

Escopo Permissão Caso de Uso
openid Autenticação OIDC Obrigatório para todos os aplicativos
profile Informações de perfil do usuário Diretório do provedor
patient/Patient.read Ler dados demográficos do paciente Pesquisa de paciente
patient/Observation.read Ler observações Sinais vitais, exames laboratoriais
patient/Condition.read Ler condições Listas de problemas
patient/MedicationRequest.read Ler medicações Histórico de medicação
patient/*.read Ler todos os recursos do paciente Dados completos do paciente
user/*.read Ler todos os recursos acessíveis Visão do provedor
offline_access Atualizar token Sessões de longa duração

Fazendo Requisições 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(`Erro FHIR: ${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 após o callback OAuth
const fhirClient = new FHIRClient(tokens.accessToken, 'https://fhir.epic.com');
const patient = await fhirClient.getPatient(tokens.patientId);
Enter fullscreen mode Exit fullscreen mode

Operações em Lote e Transacionais

Requisições em Lote

Execute requisições paralelas:

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 - Buscar múltiplos 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(`Resposta ${index}: ${entry.response.status}`);
  console.log(entry.resource);
});
Enter fullscreen mode Exit fullscreen mode

Requisições Transacionais

Execute múltiplas operações de forma 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 - Criar paciente e condição atômica
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' }
    }
  }
]);
Enter fullscreen mode Exit fullscreen mode

Assinaturas e Webhooks

Assinaturas FHIR (R4B+)

Assine eventos de mudança:

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 - Assinar novos resultados de laboratório
const subscription = await createSubscription({
  criteria: 'DiagnosticReport?category=laboratory&patient=12345',
  reason: 'Monitorar resultados de laboratório do paciente',
  endpoint: 'https://myapp.com/webhooks/fhir'
});
Enter fullscreen mode Exit fullscreen mode

Lidando com 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 referência da assinatura
  if (notification.subscription !== expectedSubscription) {
    return res.status(401).send('Não autorizado');
  }

  // Processar notificação
  if (notification.event?.resourceType === 'DiagnosticReport') {
    const reportId = notification.event.resourceId;
    const report = await fhirRequest(`/DiagnosticReport/${reportId}`);

    // Processar novo resultado de laboratório
    await processLabResult(report);
  }

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

Solução de Problemas Comuns

Problema: 401 Não Autorizado

Sintomas: Erro “Não Autorizado” ou “Token inválido”.

Soluções:

  1. Verifique se o token não expirou.
  2. Confirme escopos do token.
  3. Cabeçalho Authorization: Bearer {token} deve estar presente.
  4. A URL FHIR deve coincidir com a audiência do token.

Problema: 403 Proibido

Sintomas: Token válido, acesso negado.

Soluções:

  1. Usuário tem permissão para o recurso?
  2. Contexto do paciente corresponde?
  3. Escopos SMART cobrem a operação?
  4. Verifique controles de acesso no recurso.

Problema: 404 Não Encontrado

Sintomas: Recurso não existe ou endpoint incorreto.

Soluções:

  1. ID do recurso correto?
  2. URL base FHIR correta?
  3. Tipo de recurso suportado?
  4. Endpoint da versão correta (R4 vs R4B)?

Problema: 422 Entidade Não Processável

Sintomas: Erros de validação ao criar/atualizar.

Debug:

// Analisar erros de validação
const error = await response.json();
error.issue?.forEach(issue => {
  console.log(`Severidade: ${issue.severity}`);
  console.log(`Localização: ${issue.expression?.join('.')}`);
  console.log(`Mensagem: ${issue.details?.text}`);
});
Enter fullscreen mode Exit fullscreen mode

Causas comuns:

  • Campos obrigatórios ausentes
  • Códigos inválidos
  • Referência incorreta
  • Formato de data inválido

Lista de Verificação para Implantação em Produção

Antes de liberar, valide:

  • [ ] OAuth 2.0 + SMART on FHIR configurados
  • [ ] Lógica de atualização de token
  • [ ] Tratamento de erros robusto
  • [ ] Logging sem PHI sensível
  • [ ] Limitação de taxa
  • [ ] Retentativa com backoff exponencial
  • [ ] Testes com múltiplos EHRs
  • [ ] Validação com FHIR Validator
  • [ ] Documentação completa das operações
  • [ ] Monitoramento e alertas
  • [ ] Runbook para incidentes

Validação 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(`Erro de Validação: ${issue.message}`);
      console.error(`Localização: ${issue.path}`);
    });
    throw new Error('Falha na validação do recurso');
  }

  return true;
};

// Uso antes de criar/atualizar
await validateResource(patientResource);
Enter fullscreen mode Exit fullscreen mode

Casos de Uso no Mundo Real

Integração com Portal do Paciente

  • Desafio: Acesso a prontuários de múltiplos provedores.
  • Solução: Aplicativo SMART on FHIR integrado a Epic e Cerner.
  • Resultado: 80% de adoção, 50% menos solicitações de prontuários.

Implementação:

  • Lançamento SMART para paciente
  • Somente leitura: Patient, Observation, Condition, MedicationRequest
  • Sessão persistente via atualização de token
  • UI responsiva para mobile

Suporte à Decisão Clínica

  • Desafio: Perda de oportunidades de cuidados preventivos.
  • Solução: Consultas FHIR em tempo real para gap de cuidados.
  • Resultado: +25% nas pontuações HEDIS.

Implementação:

  • App SMART para provedores
  • Consulta a Patient, Condition, Observation, Immunization
  • Cálculo de lacunas via guidelines
  • Recomendações dentro do EHR

Análise de Saúde da População

  • Desafio: Dados incompletos entre redes.
  • Solução: Exportação em massa FHIR para análise.
  • Resultado: Visão 360º do paciente, custos PMPM menores.

Implementação:

  • Uso do FHIR Bulk Data ($export)
  • Exportações noturnas para data warehouse
  • Modelos de risco e alertas integrados

Conclusão

HL7 FHIR é a base para integração moderna em saúde. Para uma integração pronta para produção:

  • Use FHIR R4 para APIs de saúde.
  • Implemente OAuth 2.0 via SMART on FHIR.
  • Modele dados com recursos padronizados.
  • Utilize busca flexível e operações em lote/transação.
  • Apidog agiliza testes e documentação FHIR.

Seção de Perguntas Frequentes

Para que é usado o HL7 FHIR?

Permite troca padronizada de dados de saúde entre EHRs, portais, apps mobile e sistemas de TI. Casos comuns: apps para paciente, SDS, saúde populacional, coordenação de cuidados.

Como faço para começar com o FHIR?

Acesse um servidor público (ex: HAPI FHIR) ou configure um serviço cloud (Azure, AWS). Pratique leituras e buscas de recursos.

Qual é a diferença entre HL7 v2 e FHIR?

HL7 v2 usa mensagens delimitadas (ADT, ORM, ORU); FHIR usa RESTful/JSON/XML orientado a recursos. FHIR é mais simples para web/mobile.

O FHIR é compatível com HIPAA?

Depende da implementação: criptografia, autenticação, controle de acesso, audit trail. Use OAuth 2.0 + SMART para segurança.

O que são escopos SMART?

Definem permissões granulares para recursos FHIR (ex: patient/Observation.read). Solicite apenas o necessário.

Como faço para buscar recursos no FHIR?

GET com query params: /Patient?name=Smith&birthdate=ge1980-01-01. Modificadores suportados: :exact, :contains, prefixos gt, lt, etc.

O que é FHIR em Massa?

Bulk FHIR ($export) permite exportação assíncrona de grandes conjuntos de dados em NDJSON para análise.

Como lido com o versionamento do FHIR?

Aponte para uma versão específica do FHIR (R4 recomendado) e valide via CapabilityStatement.

Posso estender o FHIR com campos personalizados?

Sim, use extensões FHIR e registre no Implementation Guide.

Quais ferramentas ajudam no desenvolvimento FHIR?

HAPI FHIR, FHIR Validator, coleções Postman e Apidog para testes/documentação.


Top comments (0)