FHIR Programming using .NET - Deleting FHIR Resources

Introduction

Welcome back to our ongoing series on FHIR Programming using .NET. In the previous article, we explored how to update FHIR resources, which is essential for keeping healthcare data accurate and up-to-date. Now, we will discuss how to delete FHIR resources. Deleting resources is a critical part of managing healthcare data, allowing you to remove outdated or incorrect information from your FHIR server.

In this tutorial, we'll guide you through the process of deleting a FHIR resource using the FHIR .NET SDK. We'll cover both straightforward deletions by ID and more complex conditional deletions, equipping you with the skills to effectively manage healthcare data in your applications.

Understanding FHIR Delete Semantics

Before implementing delete operations, it's important to understand how FHIR handles deletions and the implications for data integrity.

Hard Delete vs Soft Delete

FHIR servers can implement deletions in different ways:

Soft Delete (Logical Delete) - The most common approach:

  • The resource is marked as deleted but not physically removed from storage
  • Subsequent read requests return a 410 Gone response
  • The resource's history is preserved and can still be accessed via _history
  • The deletion can potentially be undone (server-dependent)
  • Supports audit trails and regulatory compliance requirements

Hard Delete (Physical Delete) - Complete removal:

  • The resource and all its versions are permanently removed from storage
  • No history is retained - the resource is completely gone
  • Cannot be undone
  • May be required for GDPR "right to erasure" or similar privacy regulations
  • Often requires special permissions or configuration

Azure API for FHIR supports soft delete by default. Hard delete operations typically require special configuration or separate API calls.

// Soft delete (standard)
await fhirClient.DeleteAsync("Patient/123");

// Hard delete/purge - Azure specific
// Requires $purge-history operation or hardDelete parameter
// Check Azure documentation for current implementation

Referential Integrity

One of the most important considerations when deleting FHIR resources is referential integrity - what happens to other resources that reference the deleted resource?

Common Scenarios:

  • Patient with Observations - If you delete a Patient, what happens to their Observations that reference that Patient?
  • Practitioner with Encounters - Deleting a Practitioner who is referenced in multiple Encounters
  • Organization hierarchy - Deleting an Organization that is the parent of other Organizations

Server Behaviors:

  • Prevent Deletion - Server refuses to delete resources that are referenced by others (returns 409 Conflict)
  • Allow Broken References - Server allows deletion, leaving "dangling" references that return 404 when followed
  • Cascade Delete - Server deletes the resource and all resources that reference it (rarely implemented due to safety concerns)

Most production servers prevent deletion of referenced resources by default:

try
{
    await fhirClient.DeleteAsync("Patient/123");
}
catch (FhirOperationException ex) when (ex.Status == System.Net.HttpStatusCode.Conflict)
{
    // Server refused deletion due to existing references
    Console.WriteLine("Cannot delete: Resource is referenced by other resources");
    if (ex.Outcome != null)
    {
        foreach (var issue in ex.Outcome.Issue)
        {
            Console.WriteLine($"  Issue: {issue.Diagnostics}");
        }
    }
}

Cascade Delete Operations

Some servers support cascade deletion through special parameters or operations. The availability and syntax varies by server implementation.

Warning: Cascade deletes can be dangerous and may remove more data than intended. Always verify what will be deleted before executing, and consider whether soft deletion is more appropriate.

History Retention After Deletion

With soft deletes, the resource's history is preserved:

// After deletion, reading the resource returns 410 Gone
try
{
    await fhirClient.ReadAsync<Patient>("Patient/123");
}
catch (FhirOperationException ex) when (ex.Status == System.Net.HttpStatusCode.Gone)
{
    Console.WriteLine("Resource has been deleted (410 Gone)");
}

// But history is still accessible
var history = await fhirClient.HistoryAsync("Patient/123");

foreach (var entry in history.Entry ?? new List<Bundle.EntryComponent>())
{
    if (entry.Resource is Patient historicalPatient)
    {
        Console.WriteLine($"Version: {historicalPatient.Meta?.VersionId}");
        Console.WriteLine($"Last Updated: {historicalPatient.Meta?.LastUpdated}");
    }
}

Conditional Delete Considerations

Conditional deletes based on search criteria require careful consideration:

  • No matches - Returns success (nothing to delete is not an error)
  • One match - Deletes that resource
  • Multiple matches - Behavior varies by server:
    • Some servers delete all matching resources
    • Others return an error if more than one resource matches (safer)
    • Check your server's documentation for its specific behavior

Regulatory and Compliance Considerations

Healthcare data deletion is subject to regulatory requirements:

  • HIPAA - Requires retention of medical records for specified periods; deletion must be documented
  • GDPR - Requires "right to erasure" capability; must balance against medical record retention laws
  • Audit Requirements - Many jurisdictions require audit trails that persist even after data deletion

Always consult with compliance experts before implementing delete functionality in production healthcare systems.

Prerequisites

Before proceeding, ensure you have the following set up:

  • An operational FHIR server with resources available for deletion.
  • The .NET SDK installed, available from the official .NET website.
  • Visual Studio installed, which you can download from the Visual Studio website.
  • You can find all the code demonstrated in this tutorial on GitHub here

“The only way to make sense out of change is to plunge into it, move with it, and join the dance.” ~ Alan Watts

Step 1 of 2: Deleting a FHIR Resource by ID

To delete a FHIR resource, you'll typically use its unique identifier. The following example demonstrates how to delete a `Patient` resource using the FHIR .NET SDK:

using Hl7.Fhir.Model;
using Hl7.Fhir.Rest;

class Program
{
    static async Task Main(string[] args)
    {
        // Replace with your FHIR server base URL
        string fhirServerUrl = "http://hapi.fhir.org/baseR4";

        var settings = new FhirClientSettings
        {
            PreferredFormat = ResourceFormat.Json,
            ReturnPreference = ReturnPreference.Representation
        };

        var fhirClient = new FhirClient(fhirServerUrl, settings);

        try
        {
            // First, search for a patient to delete (get a valid ID)
            var searchResult = await fhirClient.SearchAsync<Patient>(
                new SearchParams().LimitTo(1));

            if (searchResult?.Entry?.Count == 0)
            {
                Console.WriteLine("No patients found to delete.");
                return;
            }

            var patientToDelete = searchResult.Entry[0].Resource as Patient;
            var patientId = patientToDelete?.Id;
            Console.WriteLine($"Found patient with ID: {patientId}");

            // Delete the Patient resource by its ID using async/await
            await fhirClient.DeleteAsync($"Patient/{patientId}");

            // Output confirmation
            Console.WriteLine("Patient resource deleted successfully.");
        }
        catch (FhirOperationException ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            if (ex.Status == System.Net.HttpStatusCode.NotFound)
            {
                Console.WriteLine("The patient resource was not found.");
            }
        }
    }
}

In this snippet, the `Patient` resource with the ID "example-patient-id" is deleted from the FHIR server. Once the deletion is performed, a confirmation message is printed, indicating the operation was successful.

Step 2 of 2: Performing Conditional Deletes

In some cases, you might need to delete resources based on certain criteria rather than a specific ID. Conditional deletes allow you to specify a condition, and all resources matching that condition will be deleted. Below is an example of how to perform a conditional delete:

// Conditional delete using async/await
var searchParams = new SearchParams().Where("identifier=example-identifier");
await fhirClient.DeleteAsync("Patient", searchParams);
Console.WriteLine("Conditional deletion completed based on identifier.");

This example shows how to delete all `Patient` resources that have a matching identifier. Conditional deletes are powerful, but should be used with caution to avoid unintended data loss.

Conclusion

In this article, we've covered the process of deleting FHIR resources using .NET and the Azure FHIR Server. Whether you're performing a simple deletion by resource ID or a more complex conditional deletion, these operations are crucial for maintaining the integrity and accuracy of your healthcare data.

This tutorial completes our series on CRUD operations with FHIR resources in .NET. With the knowledge gained, you are now fully equipped to manage FHIR resources effectively in your healthcare applications. For more advanced topics and deeper insights into FHIR programming, continue exploring the next tutorial in this series.