Subscriptions

Last updated 2 months ago

Aidbox implements FHIR Subscriptions API to notify interested 3rd parties about newly created/updated resources which meet certain criteria. Additionally to Subscription's standard push-based delivery mechanism to the endpoint specified in Subscription.channel, Aidbox provides long-polling endpoint as a $poll operation on Subscription resource.

Long-Polling Subscription Notifications

Long-Polling is a robust and easy-to-implement mechanism to get instant notification about CRUD event through HTTP protocol. The client makes a request to get the data (notifications) he hasn't already received. If the server has newer notifications for the client, he responds with them. But when no new notifications are available for the client, instead of responding with empty body, server keeps HTTP connection open for some time and waits for event to happen, then responds with notification about the event happened. If the request was interrupted (because of timeout, for example), client issues same request again.

As a response, $poll endpoint returns a FHIR Bundle containing matched resources. Every resource has a meta element with versionId and lastUpdated keys. The versionId element is quite important in context of polling because it contains a transaction ID of operation which caused the event. It's an integer and it's always increasing which means if event B happened after event A, then B's versionId will be greater than A's versionId. Client can specify last versionId it received with the from parameter in the request's query string. In this case, the $poll endpoint will return only notifications which have versionId greater than number provided in the from parameter. This approach guarantees that client will never miss a notification because of time spans between requests to the $poll endpoint. If a client polls with from=0, Aidbox will return all the notifications ever happened.

To be able to call the $poll operation, the Subscription resource should be already created, it should have Subscription.status equal to active and Subscription.criteria should specify a resource type (i.e. Observation or Observation?code=xxxx). If Subscription resource does not meet those requirements, the$poll operation will return403 Invalid Request response with OperationOutcome resource containing error message.

To summarize the polling logic, here is the JavaScript-like client-side pseudo-code:

let lastVersionId = null;
while (true) {
// During the first request(s) `from` parameter will be blank
// and Aidbox will return most recent notification.
// All subsequent requests will provide `from` parameter.
let response = httpGet('/fhir/Subscription/mysub/$poll', { from: lastVersionId });
if (response.status == 200) {
console.log("Got new notifications: ", response.body.entry.map(e => e.resource));
// NB we need to get *maximum* versionId from the response
// and update lastVersionId with it
lastVersionId = Math.max(response.body.entry.map(e => e.resource.meta.versionId));
} else {
// something went wrong, break polling loop
console.log("Error during poll request", response);
break;
}
}

Example request to the $poll operation to get all notifications since transaction 137:

Request
Response
GET /fhir/Subscription/test/$poll?from=137
Content-Type: application/json
{
"resourceType": "Bundle",
"entry": [
{
"resource": {
"name": [
{
"given": [
"mike"
],
"family": "lapshin"
}
],
"id": "75790d70-4889-4485-a67e-f40bbcb94ae2",
"meta": {
"lastUpdated": "2018-11-09T16:14:15.640Z",
"versionId": "141",
"tag": [
{
"system": "https://aidbox.app",
"code": "created"
}
]
},
"resourceType": "Patient"
}
},
{
"resource": {
"name": [
{
"given": [
"mike"
],
"family": "lapshin"
}
],
"id": "22bc8414-4b10-4d04-a237-4e7e6ea7d55e",
"meta": {
"lastUpdated": "2018-11-12T15:58:34.401Z",
"versionId": "142",
"tag": [
{
"system": "https://aidbox.app",
"code": "created"
}
]
},
"deceasedBoolean": false,
"resourceType": "Patient"
}
}
],
"type": "collection"
}

REST Hook

Configuration of Subscription with REST hook is described in the FHIR documentation.

Subscription with channel type rest-hook should be created. All headers provided with channel will be attached to the request to endpoint. If the payload field is omitted, the request will not contain body.

Request
Response
POST /fhir/Subscription
Content-Type: application/json
{
"criteria": "Patient?name=subscription",
"reason": "test",
"channel": {
"payload": "application/fhir+json",
"type": "rest-hook",
"header": [
"DEMO: DEMO",
"Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l"
],
"endpoint": "https://aidbox.requestcatcher.com/subscription"
},
"resourceType": "Subscription",
"status": "active"
}
{
"reason": "test",
"status": "active",
"channel": {
"type": "rest-hook",
"header": [
"DEMO: DEMO",
"Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l"
],
"payload": "application/fhir+json",
"endpoint": "https://aidbox.requestcatcher.com/subscription"
},
"criteria": "Patient?name=subscription",
"id": "69c7c227-c3fd-491a-a42a-59a2d7a50509",
"resourceType": "Subscription",
"meta": {
"lastUpdated": "2018-11-16T17:25:15.804Z",
"versionId": "1",
"tag": [
{
"system": "https://aidbox.app",
"code": "created"
}
]
}
}

If a subscription is saved and is active, each resource that satisfies the criteria will trigger the hook.

Request
Response
POST /fhir/Patient
{
"resourceType": "Patient",
"name": [
{
"family": "subscription"
}
]
}
{
"name": [
{
"family": "subscription"
}
],
"id": "d2831faf-c15d-428a-8256-beeb8e679af6",
"resourceType": "Patient",
"meta": {
"lastUpdated": "2018-11-16T18:50:34.255Z",
"versionId": "1",
"tag": [
{
"system": "https://aidbox.app",
"code": "created"
}
]
}
}

For example, our Subscription uses aidbox.requestcatcher.com as endpoint. If you open this URL and PUT new resource, you will see the result of hook execution:

Request
Response

Open https://aidbox.requestcatcher.com, then perform PUT of Patient

POST /subscription HTTP/1.1
Host: aidbox.requestcatcher.com
Accept-Encoding: gzip, deflate
Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
Content-Length: 225
Content-Type: application/fhir+json
Demo: DEMO
User-Agent: http-kit/2.0
{
"name": [
{
"family": "subscription"
}
],
"id": "d2831faf-c15d-428a-8256-beeb8e679af6",
"resourceType": "Patient",
"meta": {
"lastUpdated": "2018-11-16T18:50:34.255Z",
"versionId": "1",
"tag": [
{
"system": "https://aidbox.app",
"code": "created"
}
]
}
}