Skip to main content

FHIR Client

FhirClientFactoryโ€‹

FhirClientFactory.forContext(SmartLaunchContext ctx) creates a HAPI IGenericClient configured for the current session:

@Component
public class FhirClientFactory {

private final FhirContext fhirCtx = FhirContext.forR4();
private final FhirProperties props;

public IGenericClient forContext(SmartLaunchContext ctx) {
IGenericClient client = fhirCtx.newRestfulGenericClient(ctx.getFhirBaseUrl());

// Bearer token on every request
client.registerInterceptor(
new BearerTokenInterceptor(ctx.getAccessToken()));

// Timeouts
fhirCtx.getRestfulClientFactory()
.setConnectTimeout(props.getConnectTimeoutMs());
fhirCtx.getRestfulClientFactory()
.setSocketTimeout(props.getSocketTimeoutMs());

return client;
}
}

Making FHIR callsโ€‹

Read a Patientโ€‹

IGenericClient client = fhirClientFactory.forContext(ctx);
Patient patient = client
.read()
.resource(Patient.class)
.withId(ctx.getPatientId())
.execute();

Search for Conditionsโ€‹

Bundle bundle = client
.search()
.forResource(Condition.class)
.where(Condition.PATIENT.hasId(ctx.getPatientId()))
.sort().descending(Condition.RECORDED_DATE)
.returnBundle(Bundle.class)
.execute();

List<Condition> conditions = bundle.getEntry().stream()
.map(e -> (Condition) e.getResource())
.toList();

Search for Medicationsโ€‹

Bundle bundle = client
.search()
.forResource(MedicationRequest.class)
.where(MedicationRequest.PATIENT.hasId(ctx.getPatientId()))
.where(MedicationRequest.STATUS.exactly().code("active"))
.returnBundle(Bundle.class)
.execute();

Error handlingโ€‹

HAPI throws typed exceptions. Catch ResourceNotFoundException for 404, AuthenticationException for 401 (token expired before refresh ran):

try {
Patient patient = client.read()...execute();
} catch (ResourceNotFoundException e) {
throw new PatientNotFoundException(ctx.getPatientId());
} catch (AuthenticationException e) {
// TokenRefreshFilter should have handled this;
// if we get here, force a re-launch
return "redirect:/launch?error=token_expired";
}

Timeout defaultsโ€‹

PropertyDefaultDescription
smart.fhir.connect-timeout-ms10000TCP connection timeout
smart.fhir.socket-timeout-ms30000Socket read timeout

Increase socket-timeout-ms if Epic FHIR searches are slow on large patient populations.


Next: OIDC & User Profile โ†’