FhirContext - The Foundation
The FhirContext is the central starting point for all HAPI FHIR operations. It’s a thread-safe, heavyweight object that should be created once and reused. During initialization, FhirContext loads and parses all FHIR resource definitions, which is a memory-intensive and time-consuming process. By creating a single instance and sharing it across your application, you avoid this overhead and improve performance significantly. Consider storing it as a static final field or using dependency injection.
import ca.uhn.fhir.context.FhirContext;
public class FhirContextExample {
// Create a context for R4 - do this once!
private static final FhirContext ctx = FhirContext.forR4();
// For R5
// private static final FhirContext ctx = FhirContext.forR5();
// For DSTU3
// private static final FhirContext ctx = FhirContext.forDstu3();
public static FhirContext getContext() {
return ctx;
}
}
Important: Creating a FhirContext is expensive. Always reuse it!
Creating a FHIR Client
The FHIR client allows you to interact with FHIR servers. HAPI FHIR provides IGenericClient, a fluent API for all standard FHIR operations including create, read, update, delete, and search. You can configure timeouts, disable server validation for faster startup, and add interceptors for logging, authentication, or custom header injection. The client handles JSON/XML serialization, HTTP communication, and error handling automatically.
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
public class FhirClientFactory {
private static final FhirContext ctx = FhirContext.forR4();
public static IGenericClient createClient(String serverBase) {
// Disable server validation (optional - for faster startup)
ctx.getRestfulClientFactory()
.setServerValidationMode(ServerValidationModeEnum.NEVER);
// Set connection timeout (optional)
ctx.getRestfulClientFactory().setConnectTimeout(20 * 1000);
ctx.getRestfulClientFactory().setSocketTimeout(20 * 1000);
// Create the client
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// Add logging interceptor (optional - for debugging)
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
loggingInterceptor.setLogRequestSummary(true);
loggingInterceptor.setLogRequestBody(true);
client.registerInterceptor(loggingInterceptor);
return client;
}
public static void main(String[] args) {
// Example: Connect to public HAPI test server
String serverBase = "http://hapi.fhir.org/baseR4";
IGenericClient client = createClient(serverBase);
System.out.println("FHIR Client created successfully!");
System.out.println("Server: " + serverBase);
}
}
Parsing and Encoding Resources
HAPI FHIR provides parsers for both JSON and XML formats. Parsers are lightweight objects that can be created as needed, unlike FhirContext which should be reused. Encoding converts FHIR resources to string representations for transmission or storage, while parsing reconstructs resources from these strings. HAPI handles all the complexity of serialization, including extensions, contained resources, and proper formatting.
JSON Parsing and Encoding
JSON is the most common format for modern FHIR applications due to its compactness and JavaScript compatibility. The JSON parser creates human-readable output when pretty-printing is enabled, making it easier to debug and inspect resources during development.
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
public class ParsingExample {
private static final FhirContext ctx = FhirContext.forR4();
public static void demonstrateParsing() {
// Create a patient
Patient patient = new Patient();
patient.addName()
.setFamily("Smith")
.addGiven("John")
.addGiven("Michael");
patient.setGender(AdministrativeGender.MALE);
// Create a JSON parser
IParser jsonParser = ctx.newJsonParser();
jsonParser.setPrettyPrint(true);
// Encode to JSON
String jsonString = jsonParser.encodeResourceToString(patient);
System.out.println("Encoded Patient (JSON):");
System.out.println(jsonString);
// Parse back from JSON
Patient parsedPatient = jsonParser.parseResource(Patient.class, jsonString);
System.out.println("\nParsed patient name: " +
parsedPatient.getNameFirstRep().getNameAsSingleString());
}
}
XML Parsing and Encoding
XML remains important for systems that require it, particularly legacy healthcare systems and certain government regulations. The XML parser follows the same API as the JSON parser, making it easy to switch formats when needed. Both parsers produce fully compliant FHIR output that can be validated against the specification.
public class XmlParsingExample {
private static final FhirContext ctx = FhirContext.forR4();
public static void demonstrateXmlParsing() {
Patient patient = new Patient();
patient.addName().setFamily("Doe").addGiven("Jane");
// Create an XML parser
IParser xmlParser = ctx.newXmlParser();
xmlParser.setPrettyPrint(true);
// Encode to XML
String xmlString = xmlParser.encodeResourceToString(patient);
System.out.println("Encoded Patient (XML):");
System.out.println(xmlString);
// Parse back from XML
Patient parsedPatient = xmlParser.parseResource(Patient.class, xmlString);
System.out.println("\nParsed patient: " +
parsedPatient.getNameFirstRep().getNameAsSingleString());
}
}
Parser Options
HAPI FHIR parsers offer many configuration options to control output format and content. These options help optimize bandwidth by removing unnecessary elements, protect privacy by excluding sensitive data, and improve readability for debugging. Understanding these options is important for production systems where performance and data minimization matter.
public class ParserOptionsExample {
private static final FhirContext ctx = FhirContext.forR4();
public static void demonstrateParserOptions() {
IParser parser = ctx.newJsonParser();
// Pretty printing
parser.setPrettyPrint(true);
// Summary mode (omit large/narrative elements)
parser.setSummaryMode(true);
// Suppress narrative
parser.setSuppressNarratives(true);
// Override resource ID (useful for bundles)
parser.setOverrideResourceIdWithBundleEntryFullUrl(true);
// Don't encode certain elements
parser.setDontEncodeElements(Set.of("text", "meta"));
// Encode specific elements only
parser.setEncodeElements(Set.of("Patient.name", "Patient.gender"));
}
}
Client Interceptors
Interceptors allow you to modify requests and responses transparently. They follow the interceptor pattern, enabling cross-cutting concerns like authentication, logging, and metrics collection without modifying your core business logic. HAPI provides built-in interceptors for common needs like basic auth and bearer tokens, and you can create custom interceptors for specialized requirements like request signing or audit logging.
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
public class InterceptorExample {
public static IGenericClient createAuthenticatedClient(String serverBase) {
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// Basic authentication
client.registerInterceptor(
new BasicAuthInterceptor("username", "password")
);
// Or Bearer token authentication
// client.registerInterceptor(
// new BearerTokenAuthInterceptor("your-oauth-token")
// );
return client;
}
}
Best Practices Summary
| Practice | Recommendation |
|---|---|
| FhirContext | Create once, reuse everywhere |
| Parsers | Create as needed, they’re lightweight |
| Clients | Reuse when possible |
| Interceptors | Add logging in development |
| Timeouts | Always configure appropriate timeouts |
Related Articles
Explore these practical FHIR programming tutorials for hands-on examples:
- FHIR Programming using Java HAPI - Setting Up Your Environment - Detailed environment setup guide
- FHIR Programming using Java HAPI - Creating FHIR Resources - Creating resources with HAPI
- FHIR Programming using Java HAPI - Reading FHIR Resources - Reading and retrieving resources
- FHIR Programming using .NET - Introduction to Libraries - .NET alternative for FHIR development