On this page:

5.12Deleting Data

 

The FHIR delete operation performs a "logical" delete. This means that data is not physically removed from the database.

For example, suppose a Patient resource with ID 123 is created (via an HTTP POST /Patient and subsequently deleted (via an HTTP DELETE Patient/123). This will cause a second version of the Patient/123 resource to be created with version Patient/123/_history/2 that is marked as deleted.

This patient will no longer appear in search results, and attempts to read the resource (using an HTTP GET Patient/123) will fail with an "HTTP 410 Gone" response.

The original content of the resource is not destroyed however. It can still be found using two FHIR operations:

  • Using a FHIR version-specific read: GET Patient/123/_history/1
  • Using a FHIR instance-history: GET Patient/123/_history

Note that HTTP 410 Gone responses will include a Location header containing the fully qualified resource ID as well as the version ID. For example:

410 Gone
Location: http://example.org/fhir/Patient/123/_history/12

In this example, we can see that the deleted version of the resource is version 12. This means that the last non-deleted version is version 11, and this could be accessed using a version-specific read to the following URL: http://example.org/fhir/Patient/123/_history/11

5.12.1Deletes and Referential Integrity

 

A common problem – frequently in test systems but sometimes in production systems, too – is cleaning up batches of interdependent data.

For example, suppose you have a CDR containing a patient resource with the ID Patient/A. Suppose this CDR also contains Encounter/1 and Encounter/2, as well as Observation/3 and MedicationAdministration/4, and all of these resources have a reference to the resource Patient/A. We will call these resources the child resources.

If you try to DELETE Patient/A (using a standard FHIR DELETE operation), this request will be denied, assuming that there is a resource link between the child resources and the patient (a resource link is a field that is indexed with an active SearchParameter).

This will result in an HTTP 409 Conflict with a response like the following:

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "processing",
      "diagnostics": "Unable to delete Patient/1 because at least one resource has a reference to this resource. First reference found was resource Observation/2 in path Observation.subject.where(resolve() is Patient)"
    }
  ]
}

If you want to force a delete of Patient/A, you have several options:

  • You can manually delete all of the child resources before trying to delete the Patient.

  • You can disable referential integrity checking in the CDR by changing the Enforce Referential Integrity on Delete setting on the FHIR Storage module. This means that any delete will be permitted as long as the user has appropriate permissions to actually perform the delete, even if other resources still have references left. This can be confusing for other users, since it leaves resources with invalid references, but there are valid cases for doing so.

  • You can use a transactional delete

  • You can use cascading deletes

5.12.2Transactional Delete

 

The FHIR Transaction operation can be used to delete multiple resources at the same time.

This is useful if you have chains or collections of resources to delete at ones, but also can be used to delete circular references.

To delete multiple resources in a transaction, POST a Bundle such as the following to the root of your FHIR endpoint.

{
  "resourceType": "Bundle",
  "type": "transaction",
  "entry": [
    {
      "request": {
        "method": "DELETE",
        "url": "Organization/1"
      }
    },
    {
      "request": {
        "method": "DELETE",
        "url": "Organization/2"
      }
    }
  ]
}

5.12.3Cascading Deletes

 

With cascading deletes enabled, a user can perform the delete on Patient/A (per the example above) and all of the child resources will be deleted as well.

In order to perform a cascading delete, three things must occur:

First, the Cascading Deletes Enabled setting must be enabled on the FHIR Storage module.

Then, the user performing the operation must have the FHIR_DELETE_CASCADE_ALLOWED permission, as well as a specific permission allowing the child resource to be deleted. For example, you might grant the user the FHIR_DELETE_CASCADE_ALLOWED and FHIR_ALL_DELETE permissions.

Finally, to perform a cascaded delete, the client HTTP request must include either a special URL parameter (_cascade) or a special header to indicate that a cascading delete is desired.

The following example shows a delete using a URL parameter:

DELETE /Patient/123?_cascade=delete

The following example shows a delete using an HTTP header:

DELETE /Patient/123
X-Cascade: delete

5.12.4The $expunge Operation

 
This method requires specific permissions in order to use different features. See the table below for information on which permissions are needed.

In some cases, it is desirable to truly delete data. This might be because it was truly entered in error and should not be seeen, because it represents a privacy concern to leave it in place, or because your solution does not require long term retention of stale data.

The $expunge operation is a powerful operation that can physically delete old versions of resources, deleted resources, or even all data in the database.

Note that this operation is globally disabled by default as it is potentially dangerous. Change the Expunge Operation Enabled setting to enable it.

Input Parameters

Name Type Usage Default Permission Required
limit Number This parameter specifies the maximum number of entries (resource versions and/or resources) that will be deleted in a single batch before exiting. 1000 N/A
expungeDeletedResources Token (boolean value) If set to true, deleted resources will be expunged (including all previous versions of the resource). false FHIR_EXPUNGE_DELETED
expungePreviousVersions Token (boolean value) If set to true, non-current versions of resources will be expunged. false FHIR_EXPUNGE_PREVIOUS_VERSIONS
expungeEverything Token (boolean value) If set to true, current versions of resources will also be expunged. false FHIR_EXPUNGE_EVERYTHING

Instance-Level Expunge

The $expunge operation can be invoked against a single resource instance, or even an individual version of a resource instance. If invoked at the instance level (shown below), previous versions of the resource may be deleted (if expungePreviousVersions is set to true) and the current version may be deleted (if the resource is deleted and expungeDeletedResources is set to true).

POST [base]/Patient/123/$expunge
Content-Type: application/fhir+json

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "limit",
      "valueInteger": 1000
    },{
      "name": "expungeDeletedResources",
      "valueBoolean": true
    },{
      "name": "expungePreviousVersions",
      "valueBoolean": true
    }
  ]
}

The $expunge operation can also be invoked at the instance version level (shown below). This can be used to expunge an individual version of a resource without affecting other versions.

POST [base]/Patient/123/_history/2/$expunge
Content-Type: application/fhir+json

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "expungeDeletedResources",
      "valueBoolean": true
    }
  ]
}

Type-Level Expunge

The $expunge operation can be invoked at the type level. In this mode, all resources of a given type will be processed with the same rules as at the instance level.

POST [base]/Patient/$expunge
Content-Type: application/fhir+json

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "expungeDeletedResources",
      "valueBoolean": true
    },{
      "name": "expungePreviousVersions",
      "valueBoolean": true
    }
  ]
}

System-Level Expunge

The $expunge operation can be invoked at the system level. In this mode, all resources on the server will be processed with the same rules as at the instance level.

POST [base]/$expunge
Content-Type: application/fhir+json

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "expungeDeletedResources",
      "valueBoolean": true
    },{
      "name": "expungePreviousVersions",
      "valueBoolean": true
    }
  ]
}

Drop All Data

The folowing operation will delete all data, including non-deleted resources.

POST [base]/$expunge
Content-Type: application/fhir+json

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "expungeEverything",
      "valueBoolean": true
    }
  ]
}