Smile CDR v2024.05.PRE
On this page:

6.24.1Custom Resource Types

 

The FHIR standard provides a wide variety of resource types that can meet all kinds of use cases. However, sometimes it is helpful to be able to create custom resource definitions that will reside in the same repository alongside your standard resources.

6.24.1.1Considerations When Creating a Custom Resource

Please consider the interoperability tradeoffs before you create a custom resource. FHIR provides a standard extension mechanism, and any mature FHIR client library will have no trouble parsing a resource even if it has extensions that it was not expecting.

However, an entirely custom resource type may confuse clients. Many client libraries will generate errors and/or refuse to interact with a server that includes resource types they do not understand. In particular, once a custom type has been registered with the server, the new resource custom names will appear in the server CapabilityStatement. This may confuse clients accessing your server.

6.24.2Defining Custom Resource Type Classes

 

To create a custom resource type in Smile CDR, the first step is to define a Java class for your type. See the HAPI FHIR Custom Structures documentation for an introduction to custom structures.

A sample custom resource class is shown below. Note the following:

/*-
 * #%L
 * Smile CDR - CDR
 * %%
 * Copyright (C) 2016 - 2024 Smile CDR, Inc.
 * %%
 * All rights reserved.
 * #L%
 */
package com.example.resources;

import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.StringType;

import java.util.ArrayList;
import java.util.List;

/**
 * This class is a custom resource class structure representing a Meal that was
 * prepared by a chef (Patient).
 */
@ResourceDef(name = "Meal", profile = "http://example.org/StructureDefinition/Meal")
public class Meal extends DomainResource {

   public static final Base[] EMPTY_BASE_ARRAY = new Base[0];

   @Child(name = "identifier", min = 0, max = Child.MAX_UNLIMITED)
   private List<Identifier> myIdentifier;

   @Child(name = "name")
   private StringType myName;

   @Child(
         name = "chef",
         type = {Patient.class})
   private Reference myChef;

   /**
    * By convention in HAPI FHIR, the getter for a repeating (List) field should create
    * the list if it isn't already initialized.
    */
   public List<Identifier> getIdentifier() {
      if (myIdentifier == null) {
         myIdentifier = new ArrayList<>();
      }
      return myIdentifier;
   }

   /**
    * Every field annotated with {@link Child @Child} needs a getter
    */
   public StringType getName() {
      return myName;
   }

   /**
    * Every non-List field annotated with {@link Child @Child} needs a setter too
    */
   public void setName(StringType theName) {
      myName = theName;
   }

   public Reference getChef() {
      return myChef;
   }

   public void setChef(Reference theChef) {
      myChef = theChef;
   }

   /**
    * All resource classes must implement the {@literal copy()} method.
    */
   @Override
   public DomainResource copy() {
      Meal retVal = new Meal();
      super.copyValues(retVal);
      for (Identifier next : getIdentifier()) {
         retVal.getIdentifier().add(next.copy());
      }
      retVal.myName = myName != null ? myName.copy() : null;
      retVal.myChef = myChef != null ? myChef.copy() : null;
      return retVal;
   }

   @Override
   public String fhirType() {
      return "Meal";
   }

   @Override
   public Base[] getProperty(int theHash, String theName, boolean theCheckValid) throws FHIRException {
      switch (theName) {
         case "identifier":
            return myIdentifier != null ? myIdentifier.toArray(EMPTY_BASE_ARRAY) : EMPTY_BASE_ARRAY;
         case "chef":
            return myChef != null ? new Base[] {myChef} : EMPTY_BASE_ARRAY;
         case "name":
            return myName != null ? new Base[] {myName} : EMPTY_BASE_ARRAY;
         default:
            return super.getProperty(theHash, theName, theCheckValid);
      }
   }

   @Override
   public ResourceType getResourceType() {
      return null;
   }
}

6.24.3Packaging and Deploying Your Custom Resource Types

 

Once you have created your custom resource Java classes, you should compile them into a JAR file, and place this JAR file in the customerlib/ directory within your Smile CDR installation.

An example Java project showing how to create and package some custom resource classes can be downloaded at the link below.

6.24.4Search Parameters

 

Custom resources will have no search parameters in the server by default, and will therefore not be discoverable through direct searching. Other operations such as create and read will always work, but you will need to create custom search parameters if you want to be able to discover your custom resources using searching.

Custom search parameters can always be created, as described in Custom Search Parameters.

The following example shows a simple string search parameter:

{
  "resourceType": "SearchParameter",
  "id": "meal-chef",
  "url": "http://example.org/SearchParameter/meal-chef",
  "status": "active",
  "code": "chef",
  "base": [ "Meal" ],
  "type": "reference",
  "expression": "Meal.chef",
  "target": [ "Patient" ]
}

If your custom resource type has references to other resources (whether the targets are other custom types, or standard FHIR types), you can also create reference search parameters. These will allow searching by reference, but also enables search features such as _include and chained searches to work. The following example shows a sample reference search parameter:

{
  "resourceType": "SearchParameter",
  "id": "meal-name",
  "url": "http://example.org/SearchParameter/meal-name",
  "status": "active",
  "code": "name",
  "base": [ "Meal" ],
  "type": "string",
  "expression": "Meal.name"
}