Examples: FHIR Client - Smile CDR Documentation

FHIR Client interceptor Examples

This page contains example interceptors that can be registered with the FHIR Client.

Example: Starter Client interceptor for all CLIENT_xxx pointcuts

The following example shows an interceptor that can be used as a starter Client interceptor, implementing a hook method for each available pointcut.

/*-
 * #%L
 * Smile CDR - CDR
 * %%
 * Copyright (C) 2016 - 2026 Smile CDR, Inc.
 * %%
 * All rights reserved.
 * #L%
 */
package com.smilecdr.demo.fhirclient;

import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.util.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This interceptor is intended to be used with a FHIR client (IRestfulClient, or its implementations)
 * and must be registered with it, before being called.
 * <p>
 * Example:
 * 		IGenericClient fhirClient = FhirContext.forR4().newRestfulGenericClient("http://localhost:8000/");
 * 		fhirClient.registerInterceptor(new ClientInterceptorTemplate());
 * Afterward, calls done using 'fhirClient' will be intercepted by 'ClientInterceptorTemplate'
 * and CLIENT_REQUEST and CLIENT_RESPONSE hook methods will be called.
 * </p><p>
 * Client interceptor can be useful to change the behaviour of a FHIR client before it send requests to a FHIR Endpoint,
 * by changing the url/headers/content for example,
 * or by doing some checks (on http status, headers, etc.) on the response received from a FHIR Endpoint.
 * Currently, the response received can be modified in a limited way by the Client interceptor `CLIENT_RESPONSE` hook.
 * More specifically, they can only override the HTTP response by invoking the setting on ClientResponseContext, the
 * fourth parameter of the Pointcut.
 * See ClientResponseInterceptorModificationTemplate for more details.
 * </p><p>
 * In fact, this is how the FHIR client BasicAuthInterceptor, BearerTokenAuthInterceptor, LoggingInterceptor,
 * and others are actually implemented.
 * </p>
 * Can be used as a starting point for your client interceptor.
 */
@SuppressWarnings({"unused", "EmptyTryBlock"})
@Interceptor
public class ClientInterceptorTemplate {

private static final Logger ourLog = LoggerFactory.getLogger(ClientInterceptorTemplate.class);

@Hook(Pointcut.CLIENT_REQUEST)
    public void clientRequest(IHttpRequest theHttpRequest, IRestfulClient theRestfulClient) {

ourLog.info("Interceptor CLIENT_REQUEST - started");
        StopWatch stopWatch = new StopWatch();
        try {
            // your implementation goes here
        } finally {
            ourLog.info("Interceptor CLIENT_REQUEST - ended, execution took {}", stopWatch);
        }
    }

@Hook(Pointcut.CLIENT_RESPONSE)
    public void clientResponse(
            IHttpRequest theHttpRequest, IHttpResponse theHttpResponse, IRestfulClient theRestfulClient) {

ourLog.info("Interceptor CLIENT_RESPONSE - started");
        StopWatch stopWatch = new StopWatch();
        try {
            // your implementation goes here
        } finally {
            ourLog.info("Interceptor CLIENT_RESPONSE - ended, execution took {}", stopWatch);
        }
    }
}

Copy

The following example shows an interceptor that can be used to modify an HTTP request, in this case, by wrapping an R4 Patient resource with a R4 Bundle.

/*-
 * #%L
 * Smile CDR - CDR
 * %%
 * Copyright (C) 2016 - 2026 Smile CDR, Inc.
 * %%
 * All rights reserved.
 * #L%
 */
package com.smilecdr.demo.fhirclient;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.client.apache.ModifiedStringApacheHttpResponse;
import ca.uhn.fhir.rest.client.api.ClientResponseContext;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.util.StopWatch;
import jakarta.annotation.Nonnull;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Reader;

/**
 * This interceptor is intended to be used with a FHIR client (IRestfulClient, or its implementations)
 * and must be registered with it, before being called.
 * <p>
 * Example:
 * 		IGenericClient fhirClient = FhirContext.forR4().newRestfulGenericClient("http://localhost:8000/");
 * 		fhirClient.registerInterceptor(new ClientInterceptorTemplate());
 * Afterward, calls done using 'fhirClient' will be intercepted by 'ClientResponseInterceptorModificationTemplate'
 * and CLIENT_REQUEST and CLIENT_RESPONSE hook methods will be called.
 * </p>
 * This particular interceptor illustrates how to take an existing HTTP response containing a FHIR resource and return
 * a modified copy of that response that includes said Resource within a new Bundle.
 */
@Interceptor
public class ClientResponseInterceptorModificationTemplate {

private static final Logger ourLog = LoggerFactory.getLogger(ClientResponseInterceptorModificationTemplate.class);

@Hook(Pointcut.CLIENT_RESPONSE)
    public void intercept(ClientResponseContext clientResponseContext) throws IOException {
        final IBaseResource baseResourceVersionAgnostic = parseBaseResource(clientResponseContext);

if (baseResourceVersionAgnostic == null) {
            ourLog.info("Ignoring since we could not parse the HTTP response for a Resource");
            return;
        }

if (!(baseResourceVersionAgnostic instanceof Resource)) {
            ourLog.info("Ignoring since we could not parse the HTTP response for a Resource");
            return;
        }

final Class<? extends IBaseResource> returnType = clientResponseContext.getReturnType();

// We only want to trigger further processing if the expected return type is a Bundle
        if (returnType != null && Bundle.class != returnType) {
            ourLog.info("Ignoring since the expected return type is not a Bundle");
            return;
        }

// After this point, you must commit to a FHIR version, in this case: R4
        final Resource resourceR4 = (Resource) baseResourceVersionAgnostic;

// If this is a Bundle do nothing
        if (resourceR4 instanceof Bundle) {
            ourLog.info("Ignoring since this resource is already a Bundle");
            return;
        }

final Bundle bundleR4 = bundleFromResourceR4(resourceR4);
        final String bundleR4AsJson = jsonFromBundle(clientResponseContext.getFhirContext(), bundleR4);

// Wrap the old response in such a way as the new bundle JSON will be rendered instead of the old resource
        final ModifiedStringApacheHttpResponse modifiedStringApacheHttpResponse = new ModifiedStringApacheHttpResponse(
                clientResponseContext.getHttpResponse(), bundleR4AsJson, new StopWatch());
        // Mutate the original response so that FHIR Gateway will now process the bundle in place of the patient
        clientResponseContext.setHttpResponse(modifiedStringApacheHttpResponse);
    }

private IBaseResource parseBaseResource(ClientResponseContext clientResponseContext) throws IOException {
        final IHttpResponse httpResponse = clientResponseContext.getHttpResponse();
        if (Constants.STATUS_HTTP_204_NO_CONTENT == httpResponse.getStatus()) {
            ourLog.info("There is no content in this HTTP response so doing nothing");
            return null;
        }
        final FhirContext fhirContext = clientResponseContext.getFhirContext();
        final String mimeType = httpResponse.getMimeType();

final String body = getResponseBody(httpResponse);
        if (Constants.CT_TEXT.equals(mimeType)) {
            ourLog.info("There is no FHIR resource in this HTTP response so doing nothing");
            return null;
        }

final EncodingEnum enc = EncodingEnum.forContentType(mimeType);
        if (enc == null) {
            ourLog.info("Could not find a valid mime type in this HTTP response so doing nothing");
            return null;
        }

final IParser p = enc.newParser(fhirContext);
        return p.parseResource(body);
    }

@Nonnull
    private Bundle bundleFromResourceR4(Resource iBaseResource) {
        final Bundle bundle = new Bundle();
        bundle.setType(Bundle.BundleType.SEARCHSET);
        bundle.setTotal(1);
        final Bundle.BundleEntryComponent entry = bundle.addEntry();
        entry.setResource(iBaseResource);
        return bundle;
    }

private String jsonFromBundle(FhirContext fhirContext, Bundle bundle) {
        return fhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle);
    }

private String getResponseBody(IHttpResponse theHttpResponse) throws IOException {
        // It is CRUCIAL that you call the line below or downstream code in FHIR Gateway will be unable to close the
        // InputStream
        theHttpResponse.bufferEntity();
        try (final Reader reader = theHttpResponse.createReader()) {
            return IOUtils.toString(reader);
        }
    }
}

Copy

Examples: FHIR Gateway

Examples: FHIR Client

Interceptors

Interceptors Pointcuts Starter Project Examples: FHIR Endpoints Examples: HL7v2 Endpoints Examples: FHIR Storage Examples: FHIR Gateway Examples: FHIR Client Examples: MDM Examples: Subscription Examples: Channel Import Examples: Cluster Manager Examples: App Sphere Examples: System To System Data Exchange 2024.02.01 Migration Guide

Examples: MDM