https://github.com/beda-software/aidbox-react
A set of TypeScript components and utilities to create react apps on top of AidBox
https://github.com/beda-software/aidbox-react
Last synced: 7 months ago
JSON representation
A set of TypeScript components and utilities to create react apps on top of AidBox
- Host: GitHub
- URL: https://github.com/beda-software/aidbox-react
- Owner: beda-software
- License: mit
- Created: 2019-07-05T18:08:07.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2025-05-21T09:44:50.000Z (about 1 year ago)
- Last Synced: 2025-06-13T17:07:58.728Z (12 months ago)
- Language: TypeScript
- Size: 468 KB
- Stars: 10
- Watchers: 10
- Forks: 8
- Open Issues: 18
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# aidbox-react
[](https://travis-ci.org/beda-software/aidbox-react) [](https://coveralls.io/github/beda-software/aidbox-react?branch=master)
TypeScript library consisting of set of utils, functions and [React hooks](https://reactjs.org/docs/hooks-intro.html) to work with Aidbox's [FHIR API](http://hl7.org/fhir/). Based on [axios](https://github.com/axios/axios).
So basically it is a javascript/typescript Aidbox FHIR-client.
The main difference between FHIR and Aidbox data structure in our case is Reference's format. Aidbox uses two separate fields: `resourceType` and `id` while FHIR uses `resourceType/id`. [Read more](https://docs.aidbox.app/basic-concepts/aidbox-and-fhir-formats)
# Install
Clone this repository into `src/contrib/aidbox-react`
and provide type definitions for aidbox in `src/contrib/aidbox` (see example/basic set of [Aidbox typings](https://github.com/beda-software/aidbox-react/blob/master/typings/contrib/aidbox.d.ts))
# Introduction
## RemoteData
[RemoteData](https://github.com/beda-software/aidbox-react/blob/master/src/libs/remoteData.ts) is a wrapper over data.
It could have four statuses:
* Success
* Failure
* Loading
* NotAsked
RemoteDataResult is a subset of RemoteData and it could have two statuses:
* Success
* Failure
When we make a request to a server with any of library's methods, we'll probably get RemoteData as a result. Then we can easily check what've got.
Simplified example
```TypeScript
import React from 'react';
// Your Aidbox typings. Read above in Install section of this Readme
import { Patient } from 'contrib/aidbox';
import { getFHIRResource } from 'aidbox-react/lib/services/fhir';
import { isFailure, isSuccess } from 'aidbox-react/lib/libs/remoteData';
async function loadPatientGender() {
const patientResponse = await getFHIRResource({
resourceType: 'Patient',
id: 'patient-id',
});
if (isSuccess(patientResponse)) {
return `Patient name is ${patientResponse.data.gender ?? 'unknown'}`;
}
if (isFailure(patientResponse)) {
return `
Failed to request patient,
status: ${patientResponse.status},
error : ${patientResponse.error}
`;
}
}
```
##
# Content
We consider service as a function that returns `RemoteDataResult` (`RemoteDataSuccess | RemoteDataSuccess`). For details, see `RemoteData` interface in `aidbox-react/libs/remoteData.ts`
## Available functions (services)
- service({...axiosConfig})
- FHIR-specific:
- getFHIRResource(reference)
- getFHIRResources(resourceType, params)
- getAllFHIRResources(resourceType, params)
- findFHIRResource(resourceType, params)
- saveFHIRResource(resource)
- createFHIRResource(resource)
- updateFHIRResource(resource, params)
- patchFHIRResource(resource, params)
- saveFHIRResources(resources, bundleType)
- deleteFHIRResource(resources)
- forceDeleteFHIRResource(resource)
### service({...axiosConfig})
Basic function for making requests.
```TypeScript
import { service } from 'aidbox-react/lib/services/service';
import { formatError } from 'aidbox-react/lib/utils/error';
import { isFailure, isSuccess } from 'aidbox-react/lib/libs/remoteData';
async function deleteAccount() {
const result = await service({
url: '/User/$delete-account',
method: 'POST',
});
if (isSuccess(result)) {
await logout();
} else if (isFailure(result)) {
console.error(formatError(result.error));
}
}
```
### getFHIRResource
Get resource by reference (resource type and id).
```TypeScript
import { getFHIRResource } from 'aidbox-react/lib/services/fhir';
// ...
const observationResponse = await getFHIRResource(makeReference('Observation', 'observation-id'));
```
### getFHIRResources
Get resources using [Search API](https://www.hl7.org/fhir/search.html)
Returns only first page of resources.
```TypeScript
import { getFHIRResources } from 'aidbox-react/lib/services/fhir';
// ...
const qrBundleResponse = await getFHIRResources('QuestionnaireResponse', {
subject: subject.id,
questionnaire: 'intake',
status: 'completed',
});
if (isSuccess(qrBundleResponse)) {
// Iterate over found resources
qrBundleResponse.data.entry?.forEach((bundleEntry) => {
console.log(bundleEntry.resource?.status);
});
}
```
### getAllFHIRResources
Get all found resources from all pages.
```TypeScript
import moment from 'moment';
import { getAllFHIRResources } from 'aidbox-react/lib/services/fhir';
import { formatFHIRDateTime } from 'aidbox-react/lib/utils/date';
// ...
const observationsResponse = await getAllFHIRResources('Observation', {
_sort: '-date',
_count: 500,
patient: 'patient-id',
status: 'final',
date: [`ge${formatFHIRDateTime(moment())}`],
});
```
### findFHIRResource
Uses [Search API](https://www.hl7.org/fhir/search.html) to find exactly one resource and return in (not bundle). It throws `Error('Too many resources found')` if more than one resources were found and `Error('No resources found')` if nothing were found.
```TypeScript
import { findFHIRResource } from 'aidbox-react/lib/services/fhir';
const roleResponse = await findFHIRResource('PractitionerRole', {
practitioner: 'practitioner-id',
});
```
### saveFHIRResource
Saves resource. If resource has `id` – uses `PUT` method (updates), otherwise `POST` (creates).
If you want to have more control, you can use `createFHIRResource` or `updateFHIRResource` functions.
```TypeScript
import { saveFHIRResource } from 'aidbox-react/lib/services/fhir';
// ...
const saveResponse = await saveFHIRResource({
resourceType: 'Patient',
gender: 'female',
});
if (isFailure(saveResponse)) {
console.warn('Can not create a patient: ', JSON.stringify(saveResponse.error));
}
```
### createFHIRResource(resource)
Creates resource via `POST` command.
The difference with `saveFHIRResource` is that `createFHIRResource` always use `POST`, even if resource has `id` field.
```TypeScript
const resource = {
id: '1',
resourceType: 'Patient',
};
await createFHIRResource(resource);
```
### updateFHIRResource(resource, params)
Updates resource using `PUT` request.
It's required to have either resource's id or pass `params`.
```TypeScript
const resource = {
resourceType: 'Patient',
name: [{text: 'Alex'}]
};
const searchParams = { identifier: 'alex-1' };
const updateResponse = await updateFHIRResource(resource, searchParams);
```
### patchFHIRResource(resource, params)
Use `PATCH` method to patch a resource.
It's required to have either resource's id or pass `params`.
```TypeScript
const resource = {
resourceType: 'Patient',
name: [{text: 'Jennifer'}],
gender: 'female'
};
const createResponse = await createFHIRResource(resource);
if (isSuccess(createResponse)) {
const patchResponse = await patchFHIRResource({
id: createResponse.data.id,
name: [{text: 'Monica'}]
});
}
```
### saveFHIRResources(resources, bundleType)
Save an array of resources using `POST` request.
Method for every resource will be either `PUT` (if resource's id presented) or
```TypeScript
const bundleResponse = await saveFHIRResources([
{
id: 'jennifer-1',
resourceType: 'Patient',
name: [{text: 'Jennifer'}]
},
{
resourceType: 'Patient',
name: [{text: 'Monica'}]
}
], 'transaction');
```
### deleteFHIRResource(resources)
Actually it doesn't delete a resource, just mark it as deleted by altering its status (see `inactiveMapping` list in `fhir.ts`).
```TypeScript
await deleteFHIRResource(makeReference('Patient', 'patient-id'));
```
### forceDeleteFHIRResource(resource)
Deletes resource by calling `DELETE` method.
```TypeScript
const createResponse = await createFHIRResource({
resourceType: 'Patient',
name: [{text: 'Max'}]
});
if (isSuccess(createResponse)) {
const deleteResource = await forceDeleteFHIRResource(makeReference('Patient', createResponse.data.id));
}
```
## Available hooks
- useService(serviceFn)
- usePager(resourceType, resourcesOnPage?, searchParams?)
- useCRUD(resourceType, id?, getOrCreate?, defaultResource?) - WIP
# Usage
Set baseURL and token for axios instance using `setInstanceBaseURL` and `setInstanceToken/resetInstanceToken` from `aidbox-react/services/instance`
And use hooks and services
# Examples
## Pager hook
```TSX
import * as React from 'react';
import { User } from 'shared/src/contrib/aidbox';
import { usePager } from 'src/contrib/aidbox-react/services/service';
import { isLoading, isSuccess } from 'src/contrib/aidbox-react/libs/remoteData';
import { extractBundleResources } from 'src/contrib/aidbox-react/services/fhir';
export function UserList(props: {}) {
const [resourcesResponse, pagerManager] = usePager('User', 2);
if (isLoading(resourcesResponse)) {
return
Loading...;
}
if (isSuccess(resourcesResponse)) {
const users = extractBundleResources(resourcesResponse.data).User || [];
return (
);
}
return null;
}
```
## CRUD hook
```TSX
import * as React from 'react';
import { useCRUD } from 'src/contrib/aidbox-react/hooks/crud';
import { isLoading, isSuccess } from 'src/contrib/aidbox-react/libs/remoteData';
import { Patient } from 'shared/src/contrib/aidbox';
export function UserList(props: {}) {
const [resourceResponse, crudManager] = useCRUD('Patient', 'toggle', true, {
resourceType: 'Patient',
active: false,
});
if (isLoading(resourceResponse)) {
return
Loading...;
}
if (isSuccess(resourceResponse)) {
// This is just an example
const active = resourceResponse.data.active;
return (
Active: {active ? 'Yes' : 'No'}
crudManager.handleSave({
...resourceResponse.data,
active: !active,
})
}
>
Toggle
);
}
return null;
}
```