HAPI provides a built-in mechanism for adding FHIR's RESTful Server capabilities to your applications. The HAPI RESTful Server is Servlet based, so it should be easy to deploy to any of the many compliant containers that exist.

Setup is mostly done using simple annotations, which means that it should be possible to create a FHIR compliant server quickly and easily.

Defining Resource Providers

The first step in creating a FHIR RESTful Server is to define one or more resource providers. A resource provider is a class which is able to supply exactly one type of resource to be served up.

For example, if you wish to allow your server to serve up Patient, Observation and Location resources, you will need three resource providers.

A Resource provider class must implement the IResourceProvider interface, and will contain one or more methods which have been annotated with special annotations indicating which RESTful operation that method supports. Below is a simple example of a resource provider which supports the read operation (i.e. retrieve a single resource by ID) as well as the search operation (i.e. find any resources matching a given criteria) for a specific search criteria.

/**
 * All resource providers must implement IResourceProvider
 */
public class RestfulPatientResourceProvider implements IResourceProvider {

	/**
	 * The getResourceType method comes from IResourceProvider, and must
	 * be overridden to indicate what type of resource this provider
	 * supplies.
	 */
	@Override
	public Class<Patient> getResourceType() {
		return Patient.class;
	}
	
	/**
	 * The "@Read" annotation indicates that this method supports the
	 * read operation. Read operations should return a single resource
	 * instance. 
	 * 
	 * @param theId
	 *    The read operation takes one parameter, which must be of type
	 *    IdDt and must be annotated with the "@Read.IdParam" annotation.
	 * @return 
	 *    Returns a resource matching this identifier, or null if none exists.
	 */
	@Read()
	public Patient getResourceById(@IdParam IdDt theId) {
		Patient patient = new Patient();
		patient.addIdentifier();
		patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
		patient.getIdentifier().get(0).setValue("00002");
		patient.addName().addFamily("Test");
		patient.getName().get(0).addGiven("PatientOne");
		patient.setGender(AdministrativeGenderEnum.FEMALE);
		return patient;
	}

	/**
	 * The "@Search" annotation indicates that this method supports the 
	 * search operation. You may have many different method annotated with 
	 * this annotation, to support many different search criteria. This
	 * example searches by family name.
	 * 
	 * @param theFamilyName
	 *    This operation takes one parameter which is the search criteria. It is
	 *    annotated with the "@Required" annotation. This annotation takes one argument,
	 *    a string containing the name of the search criteria. The datatype here
	 *    is StringParam, but there are other possible parameter types depending on the
	 *    specific search criteria.
	 * @return
	 *    This method returns a list of Patients. This list may contain multiple
	 *    matching resources, or it may also be empty.
	 */
	@Search()
	public List<Patient> getPatient(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamilyName) {
		Patient patient = new Patient();
		patient.addIdentifier();
		patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
		patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
		patient.getIdentifier().get(0).setValue("00001");
		patient.addName();
		patient.getName().get(0).addFamily(theFamilyName.getValue());
		patient.getName().get(0).addGiven("PatientOne");
		patient.setGender(AdministrativeGenderEnum.MALE);
		return Collections.singletonList(patient);
	}

}

Adding more Methods (Search, History, Create, etc.)

You will probably wish to add more methods to your resource provider. See RESTful Operations for lots more examples of how to add methods for various operations.

For now, we will move on to the next step though, which is creating the actual server to hold your resource providers and deploying that. Once you have this working, you might want to come back and start adding other operations.

Create a Server

Once your resource providers are created, your next step is to define a server class.

HAPI provides a class called RestfulServer, which is a specialized Java Servlet. To create a server, you simply create a class which extends RestfulServer as shown in the example below.

/**
 * In this example, we are using Servlet 3.0 annotations to define
 * the URL pattern for this servlet, but we could also
 * define this in a web.xml file.
 */
@WebServlet(urlPatterns= {"/fhir/*"}, displayName="FHIR Server")
public class ExampleRestfulServlet extends RestfulServer {

	private static final long serialVersionUID = 1L;

	/**
	 * The initialize method is automatically called when the servlet is starting up, so it can
	 * be used to configure the servlet to define resource providers, or set up
	 * configuration, interceptors, etc.
	 */
   @Override
   protected void initialize() throws ServletException {
      /*
       * The servlet defines any number of resource providers, and
       * configures itself to use them by calling
       * setResourceProviders()
       */
      List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
      resourceProviders.add(new RestfulPatientResourceProvider());
      resourceProviders.add(new RestfulObservationResourceProvider());
      setResourceProviders(resourceProviders);
   }
	
}

Plain Providers (non-resource specific)

Defining one provider per resource is a good strategy to keep code readable and maintainable, but it is also possible to put methods for multiple resource types in a provider class. Providers which do not implement the IResourceProvider (and therefore are not bound to one specific resource type) are known as Plain Providers.

A plain provider may implement any RESTful operation, but will generally need to explicitly state what type of resource it applies to. If the method directly returns a resource or a collection of resources (as in an instance read or type search operation) the resource type will be inferred automatically. If the method returns a Bundle resource, it is necessary to explicitly specify the resource type in the method annotation. The following example shows this:

public class PlainProvider {

  /**
   * This method is a Patient search, but HAPI can not automatically
   * determine the resource type so it must be explicitly stated.
   */
  @Search(type=Patient.class)
  public Bundle searchForPatients(@RequiredParam(name=Patient.SP_NAME) StringDt theName) {
    Bundle retVal = new Bundle();
    // perform search
    return retVal;
  }	
	
}

In addition, some methods are not resource specific. For example, the system history operation returns historical versions of all resource types on a server, so it needs to be defined in a plain provider.

Once you have defined your plain providers, they are passed to the server in a similar way to the resource providers.

public class ExampleServlet extends ca.uhn.fhir.rest.server.RestfulServer {

    /**
     * Constructor
     */
  public ExampleServlet() {
    /*
     * Plain providers are passed to the server in the same way
     * as resource providers. You may pass both resource providers
     * and and plain providers to the same server if you like. 
     */
    List<Object> plainProviders=new ArrayList<Object>();
    plainProviders.add(new PlainProvider());
    setPlainProviders(plainProviders);
    
    List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
    // ...add some resource providers...
    setResourceProviders(resourceProviders);
  }
	
}

Configure the Server's Identity/Web Address

The server will return data in a number of places that includes the complete "identity" of a resource. Identity in this case refers to the web address that a user can use to access the resource.

For instance, if your server is hosted at http://foo.com/fhir and your resource provider returns a Patient resource with the ID "123", the server should translate that ID to "http://foo.com/fhir/Patient/123".

The server will attempt to determine what the base URL should be based on what the request it receives looks like, but if it is not getting the right address you may wish to use a different "address strategy".

The simplest way to do this is to configure the server to use a hardcoded base URL, which means that the server won't try to figure out the "http://foo.com/fhir" part of the URL but will instead just use a fixed value you supply. This is shown in the following example:

    public class MyServlet extends ca.uhn.fhir.rest.server.RestfulServer {

        /**
         * Constructor
         */
        public MyServlet() {

            String serverBaseUrl = "http://foo.com/fhir";
            setServerAddressStrategy(new HardcodedServerAddressStrategy(serverBaseUrl));

            // ...add some resource providers, etc...
            List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
            setResourceProviders(resourceProviders);
        }

    }

Other Strategies

See the IServerAddressStrategy JavaDoc (specifically the list of "All Known Implementing Classes") to see other strategies that are available.

Deploy

Once you have created your resource providers and your restful server class, you can bundle these into a WAR file and you are ready to deploy to any JEE container (Tomcat, Websphere, Glassfish, etc).

Bundling a servlet into a WAR file and deploying it to an application server is beyond the scope of this page, but there are many good tutorials on how to do this.

Conformance/Metadata Statement

The HAPI FHIR RESTful Server will automatically export a conformance statement, as required by the FHIR Specification.

This statement is automatically generated based on the various annotated methods which are provided to the server. This behaviour may be modified by creating a new class containing a method annotated with a @Metadata Operation and then passing an instance of that class to the setServerConformanceProvider method on your server.

Enhancing the Generated Conformance Statement

If you have a need to add your own content (special extensions, etc.) to your server's conformance statement, but still want to take advantage of HAPI's automatic conformance generation, you may wish to extend ServerConformanceProvider.

In your own class extending this class, you can override the getServerConformance() method to provide your own implementation. In this method, call super.getServerConformance() to obtain the built-in conformance statement and then add your own information to it.

Note that if you are adding items during each invocation you should be aware that by default the same instance is cached by ServerConformanceProvider. This can result in an ever-growing conformance statement. You must call setCache(false); in the constructor of your new conformance provider to avoid this behaviour.

Paging Responses

The Search and History operations both return a bundle which contain zero or more resources. FHIR RESTful servers may optionaly support paging responses, meaning that (for example) if a search returns 500 resources, the server can return a bundle containing only the first 20 and a link which will return the next 20, etc.

By default, RESTful servers will not page, but will rather return all resources immediately in a single bundle. There are two complimentary parts to the paging support: paging prividers, and bundle providers.

Paging Providers

To support paging, a server must have an IPagingProvider implementation set. The paging provider is used to store resource return lists between incoming calls by clients.

A paging provider provides two key methods:

  • storeResultList, which takes a bundle provider (see below) and stores it for later retrieval. This might be by simply keeping it in memory, but it might also store it on disk, in a database, etc. This method must return a textual ID which can be used to retrieve this list later.
  • retrieveResultList, which takes an ID obtained by a previous call to storeResultList and returns the corresponding result list.

Note that the IPagingProvider is intended to be simple and implementable and you are encouraged to provide your own implementations.

The following example shows a server implementation with paging support.

public class PagingServer extends RestfulServer {

	public PagingServer() {
		
		/*
		 * Set the resource providers as always. Here we are using the paging
		 * provider from the example below, but it is not strictly neccesary
		 * to use a paging resource provider as well. If a normal resource 
		 * provider is used (one which returns List<?> instead of IBundleProvider)
		 * then the loaded resources will be stored by the IPagingProvider.
		 */
		setResourceProviders(new PagingPatientProvider());
		
		/*
		 * Set a paging provider. Here a simple in-memory implementation
		 * is used, but you may create your own. 
		 */
		FifoMemoryPagingProvider pp = new FifoMemoryPagingProvider(10);
		pp.setDefaultPageSize(10);
		pp.setMaximumPageSize(100);
		setPagingProvider(pp);
				
	}

}

Bundle Providers

If a server supports a paging provider, a further optimization is to also use a bundle provider. A bundle provider simply takes the place of the List<IResource> return type in your provider methods.

When using a bundle provider however, the server will only request small sublists of resources as they are actually being returned. This allows servers to optimize by not loading all resources into memory until they are actually needed.

One implementation of a bundle provider is shown below. This provider example works by only keeping the resource IDs in memory, but there are other possible implementation strategies that would work as well.

Note that the IBundleProvider is intended to be simple and implementable and you are encouraged to provide your own implementations.

public class PagingPatientProvider implements IResourceProvider {

   /**
    * Search for Patient resources matching a given family name
    */
   @Search
   public IBundleProvider search(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamily) {
      final InstantDt searchTime = InstantDt.withCurrentTime();

      /**
       * First, we'll search the database for a set of database row IDs that
       * match the given search criteria. That way we can keep just the row IDs
       * around, and load the actual resources on demand later as the client
       * pages through them.
       */
      final List<Long> matchingResourceIds = null; // <-- implement this

      /**
       * Return a bundle provider which can page through the IDs and return the
       * resources that go with them.
       */
      return new IBundleProvider() {

         @Override
         public int size() {
            return matchingResourceIds.size();
         }

         @Override
         public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
            int end = Math.max(theToIndex, matchingResourceIds.size() - 1);
            List<Long> idsToReturn = matchingResourceIds.subList(theFromIndex, end);
            return loadResourcesByIds(idsToReturn);
         }

         @Override
         public InstantDt getPublished() {
            return searchTime;
         }

         @Override
         public Integer preferredPageSize() {
            // Typically this method just returns null
            return null;
         }

			@Override
			public String getUuid() {
				return null;
			}
      };
   }

   /**
    * Load a list of patient resources given their IDs
    */
   private List<IBaseResource> loadResourcesByIds(List<Long> theIdsToReturn) {
      // .. implement this search against the database ..
      return null;
   }

   @Override
   public Class<? extends IResource> getResourceType() {
      return Patient.class;
   }

}

Common Method Parameters

Different RESTful methods will have different requirements in terms of the method parameters they require, as described in the RESTful Operations page.

In addition, there are several parameters you may add in order to meet specific needs of your application.

Accessing the underlying Servlet Request/Response

In some cases, it may be useful to have access to the underlying HttpServletRequest and/or HttpServletResponse objects. These may be added by simply adding one or both of these objects as method parameters.

@Search
public List<Patient> findPatients(
		@RequiredParam(name="foo") StringParam theParameter,
		HttpServletRequest theRequest, 
		HttpServletResponse theResponse) {
 List<Patient> retVal=new ArrayList<Patient>(); // populate this
 return retVal;
}

Subsetting: _summary and _elements parameters

FHIR allows for the a number of special behaviours where only certain portions of resources are returned, instead of the entire resource body. These behaviours are automatically supported in HAPI (as of HAPI 1.2) and no additional effort needs to be taken.

The following behaviours are automatically supported by the HAPI server:

Parameter Description
_summary=true Resources will be returned with any elements not marked as summary elements omitted.
_summary=text Only the narrative portion of returned resources will be returned. For a read/vread operation, the narrative will be served with a content type of text/html. for other operations, a Bundle will be returned but resources will only include the text element.
_summary=data The narrative (text) portion of the resource will be omitted.
_summary=count For a search, only Bundle.count will be returned.
_elements=[element names] Only the given top level elements of returned resources will be returned, e.g for a Patient search: _elements=name,contact

Exception/Error Handling

Within your RESTful operations, you will generally be returning resources or bundles of resources under normal operation. During execution you may also need to propagate errors back to the client for a variety of reasons.

Automatic Exception Handling

By default, HAPI generates appropriate error responses for a several built-in conditions. For example, if the user makes a request for a resource type that does not exist, or tries to perform a search using an invalid parameter, HAPI will automatically generate an HTTP 400 Invalid Request, and provide an OperationOutcome resource as response containing details about the error.

Similarly, if your method implementation throws any exceptions (checked or unchecked) instead of returning normally, the server will usually* automatically generate an HTTP 500 Internal Error and generate an OperationOutcome with details about the exception.

* Note that certain exception types will generate other response codes, as explained below.

Generating Specific HTTP Error Responses

In many cases, you will want to respond to client requests with a specific HTTP error code (and possibly your own error message too). Sometimes this is a requirement of the FHIR specification (e.g. the "validate" operation requires a response of HTTP 422 Unprocessable Entity if the validation fails). Sometimes this is simply a requirement of your specific application (e.g. you want to provide application specific HTTP status codes for certain types of errors)

To customize the error that is returned by HAPI's server methods, you must throw an exception which extends HAPI's BaseServerResponseException class. Various exceptions which extend this class will generate a different HTTP status code.

For example, the ResourceNotFoundException causes HAPI to return an HTTP 404 Resource Not Found. A complete list of available exceptions is available here.

If you wish to return an HTTP status code for which there is no pre-defined exception, you may throw the UnclassifiedServerFailureException, which allows you to return any status code you wish.

Returning an OperationOutcome for Errors

By default, HAPI will automatically generate an OperationOutcome which contains details about the exception that was thrown. You may wish to provide your own OperationOutcome instead. In this case, you may pass one into the constructor of the exception you are throwing.

@Read
public Patient read(@IdParam IdDt theId) {
   if (databaseIsDown) {
      OperationOutcome oo = new OperationOutcome();
      oo.addIssue().setSeverity(IssueSeverityEnum.FATAL).setDetails("Database is down");
      throw new InternalErrorException("Database is down", oo);
   }
   
   Patient patient = new Patient(); // populate this
   return patient;
}

Using the Server

Your RESTful server should now support the methods you have declared. Here are a few helpful tricks for interacting with the server:

Pretty Printing: The HAPI RESTful server supports a non-standard parameter called _pretty, which can be used to request that responses be pretty-printed (indented for easy reading by humans) by setting the value to true. This can be useful in testing. An example URL for this might be:
http://example.com/fhir/Patient/_search?name=TESTING&_pretty=true

Populating Resource Metadata

Server operations will often return a resource or a bundle of resources. These types will contain one or more resource instances, but also specify a set of metadata describing that resource.

For example, resources have a "published" and "updated" date, referring to the date/time the resource was originally created and the date/time the resource was last updated respectively. For operations which return a single resource, these values are returned via HTTP headers. For operations which return a bundle, these values are returned via elements within the bundle's "entry" tag.

Bundles may also contain a set of links, such as an "alternate" link to a resource, or a "search" link.

Populating these metadata elements is done via the IResource#getResourceMetadata() method. The following example shows how to set various metadata elements on a resource being returned.

@Search
public List<Patient> getAllPatients() {
   ArrayList<Patient> retVal = new ArrayList<Patient>();
   
   // Create a patient to return
   Patient patient = new Patient();
   retVal.add(patient);
   patient.setId("Patient/123");
   patient.addName().addFamily("Smith").addGiven("John");
   
   // Create a tag list and add it to the resource
   TagList tags = new TagList();
   tags.addTag(Tag.HL7_ORG_FHIR_TAG, "http://foo/tag1.html", "Some tag");
   tags.addTag(Tag.HL7_ORG_FHIR_TAG, "http://foo/tag2.html", "Another tag");
   ResourceMetadataKeyEnum.TAG_LIST.put(patient, tags);
   
   // Set some links (these can be provided as relative links or absolute)
   // and the server will convert to absolute as appropriate
   String linkAlternate = "Patient/7736";
   ResourceMetadataKeyEnum.LINK_ALTERNATE.put(patient, linkAlternate);
   String linkSearch = "Patient?name=smith&name=john";
   ResourceMetadataKeyEnum.LINK_SEARCH.put(patient, linkSearch);
   
   // Set the published and updated dates
   InstantDt pubDate = new InstantDt("2011-02-22");
   ResourceMetadataKeyEnum.PUBLISHED.put(patient, pubDate);
   InstantDt updatedDate = new InstantDt("2014-07-12T11:22:27Z");
   ResourceMetadataKeyEnum.UPDATED.put(patient, updatedDate);
   
   // Set the resource title (note that if you are using HAPI's narrative
   // generation capability, the narrative generator will often create
   // useful titles automatically, and the server will create a default
   // title if none is provided)
   String title = "Patient John SMITH";
   ResourceMetadataKeyEnum.TITLE.put(patient, title);
   
   return retVal;
}

Server Lifecycle Methods

Resource providers may optionally want to be notified when the server they are registered with is being destroyed, so that they can perform cleanup. In this case, a method annotated with the @Destroy annotation can be added (this method should be public, return void, and take no parameters).

This method will be invoked once by the RestfulServer when it is shutting down.

A Complete Example

A complete example showing how to implement a RESTful server can be found in our Git repo here: https://github.com/jamesagnew/hapi-fhir/tree/master/restful-server-example

Hopefully this will be available as a separate download soon, but currently it may be used to demonstrate a fully working server project.

Back to top

Reflow Maven skin by Andrius Velykis.