Batch & Transaction

FHIR Batch & Transaction

In general, every FHIR API request you make does just one thing. It might create a resource, or perform a search that returns matching resources. When you need to make a number of FHIR API requests, it can be slow and expensive to make an equal number of HTTP requests. This is where Batch and Transaction requests come into play.

Batch

A Batch request is a single HTTP API request that contains an array of FHIR requests to perform. The Batch type is used when there are no interdependencies between the requests being performed together. For example, creating a number of new unrelated resources should use the Batch type. If there are interdependencies between the requests, you should use the Transaction type.

Request

A FHIR Batch request takes an array of FHIR requests to perform in a single HTTP API request. The Bundle resource (opens in a new tab) is used to represent the individual requests in an array. To make a Batch request, you set Bundle.type to batch.

For example, imagine you need to populate a Schedule with 30-minute appointment slots for a week based on provider availability. You might execute this script automatically at the start of the week using a cron Zambda Function. You want to create potentially hundreds of Slot resources at the same time. You can do this with a single Batch request.

Create two Slots in a single Batch request with the v3 SDK:

import Oystehr from '@oystehr/sdk'
 
const oystehr = new Oystehr({
  accessToken: "<your_access_token>",
});
 
const response = await oystehr.fhir.batch({
  requests: [
    {
      method: 'POST',
      url: '/Slot',
      resource: {
        resourceType: 'Slot',
        schedule: {
          reference: 'Schedule/6bbdbadf-4f60-4bdd-8c54-b37970c584b2',
        },
        status: 'free',
        start: '2023-12-15T10:00:00Z',
        end: '2023-12-15T10:30:00Z',
      },
    },
    {
      method: 'POST',
      url: '/Slot',
      resource: {
        resourceType: 'Slot',
        schedule: {
          reference: 'Schedule/6bbdbadf-4f60-4bdd-8c54-b37970c584b2',
        },
        status: 'free',
        start: '2023-12-15T10:30:00Z',
        end: '2023-12-15T11:00:00Z',
      },
    },
  ],
});

Response

The API responds with 200 OK and the response body contains an array of responses for each request in the Batch request:

{
  "resourceType": "Bundle",
  "type": "batch-response",
  "entry": [
    {
      "response": {
        "outcome": {
          "resourceType": "OperationOutcome",
          "id": "created",
          "issue": [
            {
              "severity": "information",
              "code": "informational",
              "details": {
                "text": "Created"
              }
            }
          ]
        },
        "status": "201",
        "location": "Slot/36cbfbb0-7167-4710-b6dc-e75d27eade12"
      },
      "resource": {
        "resourceType": "Slot",
        "schedule": {
          "reference": "Schedule/6bbdbadf-4f60-4bdd-8c54-b37970c584b2"
        },
        "status": "free",
        "start": "2023-12-15T10:00:00Z",
        "end": "2023-12-15T10:30:00Z",
        "id": "36cbfbb0-7167-4710-b6dc-e75d27eade12",
        "meta": {
          "versionId": "80b07879-e7cc-424e-b52c-197520f9b46c",
          "lastUpdated": "2023-11-17T19:45:09.489Z"
        }
      }
    },
    {
      "response": {
        "outcome": {
          "resourceType": "OperationOutcome",
          "id": "created",
          "issue": [
            {
              "severity": "information",
              "code": "informational",
              "details": {
                "text": "Created"
              }
            }
          ]
        },
        "status": "201",
        "location": "Slot/1e841572-ca4b-4452-8844-57653135156c"
      },
      "resource": {
        "resourceType": "Slot",
        "schedule": {
          "reference": "Schedule/6bbdbadf-4f60-4bdd-8c54-b37970c584b2"
        },
        "status": "free",
        "start": "2023-12-15T10:30:00Z",
        "end": "2023-12-15T11:00:00Z",
        "id": "1e841572-ca4b-4452-8844-57653135156c",
        "meta": {
          "versionId": "ffdfb621-8096-4d94-8b7e-b7f65d228526",
          "lastUpdated": "2023-11-17T19:45:09.937Z"
        }
      }
    }
  ]
}

Transaction

Transaction requests work just like Batch requests plus a few additional features:

  • Transaction requests are atomic. If any of the requests in the Transaction fail, none of the requests are performed. Put another way, if any of the requests processed later in the Transaction fail, earlier requests are rolled back.
  • Transaction requests can contain interdependencies between requests. For example, you can create a Patient and then create an Encounter which references that Patient in the same Transaction request.

The Oystehr FHIR API performs requests in a Transaction in a specific order so that it can enforce the atomicity of the Transaction. As documented in the FHIR specification transaction processing rules (opens in a new tab), requests are processed in this order:

  1. Process any DELETE interactions
  2. Process any POST interactions
  3. Process any PUT or PATCH interactions
  4. Process any GET or HEAD interactions
  5. Resolve any conditional references

Using PATCH operations in Batch and Transactions

As shown in the FHIR Basics guide, Oystehr supports using JSON Patch for FHIR PATCH operations. However, JSON Patch operations are not valid FHIR resources on their own. To use JSON Patch with Batch and Transaction requests, you need to wrap your JSON Patch operations in a FHIR Binary resource. Please see the FHIR docs (opens in a new tab) for more information.

The v3 SDK allows you to pass JSON Patch operations directly to the batch call.
import Oystehr from '@oystehr/sdk';
 
const oystehr = new Oystehr({
  accessToken: '<your_access_token>',
});
 
// The following requests are identical
const result = await oystehr.fhir.batch({ requests: [
  {
    url: '/Patient/4e9f0f8b-e55b-48d5-a9da-a36ae918f571',
    method: 'PATCH',
    resource: {
      resourceType: 'Binary',
      contentType: 'application/json-patch+json',
      data: Buffer.from(JSON.stringify(
        [
          {
            op: 'replace',
            path: '/name',
            value: [
              {
                given: [
                  "Aegon"
                ],
                family: "Targaryen"
              }
            ],
          }
        ]
      ), 'utf8').toString('base64');
    },
  },
  {
    url: '/Patient/4e9f0f8b-e55b-48d5-a9da-a36ae918f571',
    method: 'PATCH',
    operations: [
      {
        op: 'replace',
        path: '/name',
        value: [
          {
            given: [
              "Aegon"
            ],
            family: "Targaryen"
          }
        ],
      }
    ],
  },
]});