Skip to main content

Create a Doctor-centric App with iCure

In this tutorial, you will learn to create a simple doctor-centric application which uses iCure to securely store data. By the end of it, you will be able to:

  • Instantiate a MedTech API.
  • Create a Patient.
  • Get your newly created Patient information.
  • Create observations related to your patient.
  • Get your newly created Observation information.
  • Search for observations satisfying some criteria.

Pre-requisites

To begin working with the MedTech SDK, you need to have a valid iCure User. You can obtain an iCure user in one of the following ways:

  • Launch a free iCure instance locally on your computer, which will automatically create a default user for you. To learn how to do this, refer to the QuickStart guide.
  • Register on the iCure Cockpit to get your iCure Cloud User. To learn how to do this, refer to the How to register on Cockpit guide.

The iCure User credentials and the host URL of the following steps will depend on your choice.

Initialize the project

To start working with the iCure MedTech SDK, add the following dependencies to your package.json file :

  • @icure/medical-device-sdk
  • isomorphic-fetch
  • node-localstorage

Init your MedTech API

Once you have your user credentials, you can start using the iCure MedTech SDK. Create an instance of a MedTech API object using your credentials and the desired iCure host. This api object will automatically manage the authentication to the iCure backend and give you access to all SDK services.

Provide the iCure host to use and your user credentials in this API.

import 'isomorphic-fetch'
import { webcrypto } from 'crypto'

After instantiating your API, you can now get the information of your logged user.

const loggedUser = await api.userApi.getLogged()
expect(loggedUser.login).to.be.equal(iCureUserLogin)
loggedUser
{
"id": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"rev": "2-f648b39ce5e04f1ee8e84c0e7ad77f2f",
"created": 1700058590776,
"name": "Master HCP",
"properties": {},
"roles": {},
"login": "master@3ed064.icure",
"groupId": "test-group",
"healthcarePartyId": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"sharingDataWith": {},
"email": "master@3ed064.icure",
"authenticationTokens": {}
}

At this point, your user can get data but is not able to encrypt/decrypt it. For this, you need to create and assign them an RSA keypair.

Init user cryptography

You may have noticed the withCryptoStrategies() method in the instantiation of the api. This allows the user to load his existing key pairs. If you don't have any existing key pair, you can instantiate the MedTech API without specifying them:

const apiWithoutKeys = await new EHRLiteApi.Builder()
.withICureBaseUrl(iCureHost)
.withUserName(iCureUserLogin)
.withPassword(iCureUserPassword)
.withCrypto(webcrypto as any)
.withCryptoStrategies(new SimpleEHRLiteCryptoStrategies([]))
.build()

If you do not specify a key pair during the instantiation, the API will try to retrieve it from the local storage. If the local storage contains no key, then a new pair will be created.

info

You can learn more about the Crypto Strategies here.

tip

If your solution is a web app, be conscious that if you store the keypair into the browser data and the user deletes it, they will lose his RSA keypair. Make sure to store the keypair in a secured place, and re-import it with this method.

caution

If you're working with Node and would like to store the user keypair in the local storage, you will have to initialize it first.

Example
import { LocalStorage } from 'node-localstorage'

const tmp = os.tmpdir()
console.log('Saving keys in ' + tmp)
;(global as any).localStorage = new LocalStorage(tmp, 5 * 1024 * 1024 * 1024)
;(global as any).Storage = ''

q

Now that your API is properly initialized and that your user can create encrypted data, let's check how you can create a patient with iCure.

Create your first patient

In most doctor-centric applications, you will have to create patients.

To do this, call the api.patientApi.createOrModifyPatient service.

import {
EHRLiteApi,
LocalComponent,
Observation,
Patient,
CodingReference,
ObservationFilter,
Component,
} from '@icure/ehr-lite-sdk'
createdPatient
{
"id": "405c3210-8b5f-48a1-9d58-0a3d36672102",
"rev": "1-c171d6ff15b710978fafd60a20794416",
"identifiers": [],
"created": 1700058639561,
"modified": 1700058639561,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"tags": {},
"codes": {},
"names": [
{
"family": "Snow",
"given": [
"John"
],
"prefix": [],
"suffix": [],
"text": "Snow John",
"use": "official"
}
],
"languages": [],
"addresses": [],
"gender": "male",
"birthSex": "unknown",
"mergedIds": [],
"active": true,
"deactivationReason": "none",
"personalStatus": "unknown",
"notes": [],
"relatives": [],
"patientPractitioners": [],
"patientProfessions": [],
"properties": {},
"systemMetaData": {
"hcPartyKeys": {},
"privateKeyShamirPartitions": {},
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"aesExchangeKeys": {},
"transferKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "c8cXVSz6/Fd7j0wanh2f0SWebeUF9fzlPCwS1EloQ1s=",
"publicKeysForOaepWithSha256": [],
"tags": {}
},
"firstName": "John",
"lastName": "Snow"
}
info

To call these services, you always have to fully initialize your MedTechApi and init your user cryptography

Whenever you create a new patient (or any other type of entity) iCure will automatically generate and assign a UUID to it. You will need this id to retrieve the information of your patient later.

Get your newly created Patient information

You can retrieve patient information using patientApi.getPatient with the id of the patient you want to retrieve.

const johnSnow = await api.patientApi.get(createdPatient.id)
expect(createdPatient.id).to.be.equal(johnSnow.id)
johnSnow
{
"id": "405c3210-8b5f-48a1-9d58-0a3d36672102",
"rev": "1-c171d6ff15b710978fafd60a20794416",
"identifiers": [],
"created": 1700058639561,
"modified": 1700058639561,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"responsible": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"tags": {},
"codes": {},
"names": [
{
"family": "Snow",
"given": [
"John"
],
"prefix": [],
"suffix": [],
"text": "Snow John",
"use": "official"
}
],
"languages": [],
"addresses": [],
"gender": "male",
"birthSex": "unknown",
"mergedIds": [],
"active": true,
"deactivationReason": "none",
"personalStatus": "unknown",
"notes": [],
"relatives": [],
"patientPractitioners": [],
"patientProfessions": [],
"properties": {},
"systemMetaData": {
"hcPartyKeys": {},
"privateKeyShamirPartitions": {},
"secretForeignKeys": [],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"aesExchangeKeys": {},
"transferKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "c8cXVSz6/Fd7j0wanh2f0SWebeUF9fzlPCwS1EloQ1s=",
"publicKeysForOaepWithSha256": [],
"tags": {}
},
"firstName": "John",
"lastName": "Snow"
}

If you would like to know more about the information contained in Patient, go check the References

Let's now create some medical data for our patient.

Create your first observation

You can use observations to store information related to the health of a patient. For example, when a patient goes to the doctor, we can use observations to register the symptoms they experience and/or the results of measurements taken by the doctor.

You can create a new observation with dataSampleApi.createOrModifyDataSample. If you want to create many observations at once, you should instead use dataSampleApi.createOrModifyDataSamples. Link these observations to the patient you created earlier through the patientId parameter. An observation must always be linked to a patient.

import { CodingReference, Content, DataSample } from '@icure/medical-device-sdk'

const createdData = await api.dataSampleApi.createOrModifyDataSamplesFor(johnSnow.id, [
new DataSample({
labels: new Set([new CodingReference({ type: 'LOINC', code: '29463-7', version: '2' })]),
content: { en: new Content({ numberValue: 92.5 }) },
valueDate: 20220203111034,
comment: 'Weight',
}),
new DataSample({
labels: new Set([new CodingReference({ type: 'LOINC', code: '8302-2', version: '2' })]),
content: { en: new Content({ numberValue: 187 }) },
valueDate: 20220203111034,
comment: 'Height',
}),
])
createdData
[
{
"id": "2c8af9c3-25e7-498e-a8d2-5ba6c555e305",
"identifiers": [],
"batchId": "eaeba6ed-faec-4bf2-8761-60cd28ac1745",
"healthcareElementIds": [],
"index": 0,
"component": {
"numberValue": 92.5
},
"valueDate": 20220203111034,
"openingDate": 20231115143039,
"created": 1700058639638,
"modified": 1700058639638,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"performer": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"localContent": {},
"qualifiedLinks": {},
"codes": {},
"tags": {},
"systemMetaData": {
"secretForeignKeys": [
"6b38500c-4151-4e9c-8fa4-e44de4426a11"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "wfptNWxdjhwJymY4BeXlOOT9RgTmydwW1QhRBFlSDes6k+5c5fcCMmefvxYbehdVs/QWYR4K0aPiTboeZQN41Q==",
"tags": {}
},
"notes": []
},
{
"id": "0c02ee59-489b-43ae-9bba-220750fd9430",
"identifiers": [],
"batchId": "eaeba6ed-faec-4bf2-8761-60cd28ac1745",
"healthcareElementIds": [],
"index": 1,
"component": {
"numberValue": 187
},
"valueDate": 20220203111034,
"openingDate": 20231115143039,
"created": 1700058639638,
"modified": 1700058639638,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"performer": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"localContent": {},
"qualifiedLinks": {},
"codes": {},
"tags": {},
"systemMetaData": {
"secretForeignKeys": [
"6b38500c-4151-4e9c-8fa4-e44de4426a11"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "RZEbPLO7R8cjyvnsM90V6fojaxYfwq0N8D/pQGedZmDimiRlsI/qMzFf06Dc5X6c7UPZ1znkDzxtp/gF7MQwEA==",
"tags": {}
},
"notes": []
}
]

When creating your observations, iCure will automatically assign an id to each of them, similarly to what happens for patients.

But what happens if you don't know these ids? How can you find back your data?

Search for observations satisfying some criteria

Sometimes you will want to search for data which satisfies some specific criteria.

You can do this using the filter services of the apis, such as dataSampleApi.filterDataSamples or patientApi.filterPatients. Let's say we would like to find back all medical data with the LOINC label corresponding to the body weight for the patient we have created.

import { SimpleEHRLiteCryptoStrategies } from '@icure/ehr-lite-sdk/services/EHRLiteCryptoStrategies.js'
import { GenderEnum } from '@icure/ehr-lite-sdk/models/enums/Gender.enum.js'
import { mapOf } from '@icure/typescript-common'

initLocalStorage()

const iCureHost = process.env.ICURE_URL!
const iCureUserPassword = process.env.ICURE_USER_PASSWORD!
const iCureUserLogin = process.env.ICURE_USER_NAME!
const iCureUserPubKey = process.env.ICURE_USER_PUB_KEY!
const iCureUserPrivKey = process.env.ICURE_USER_PRIV_KEY!

const apiWithKeys = await new EHRLiteApi.Builder()
.withICureBaseUrl(iCureHost)
.withUserName(iCureUserLogin)
.withPassword(iCureUserPassword)
.withCrypto(webcrypto as any)
.withCryptoStrategies(
new SimpleEHRLiteCryptoStrategies([
{ publicKey: iCureUserPubKey, privateKey: iCureUserPrivKey },
]),
)
.build()
johnData
{
"rows": [
{
"id": "2c8af9c3-25e7-498e-a8d2-5ba6c555e305",
"identifiers": [],
"batchId": "eaeba6ed-faec-4bf2-8761-60cd28ac1745",
"healthcareElementIds": [],
"index": 0,
"component": {
"numberValue": 92.5
},
"valueDate": 20220203111034,
"openingDate": 20231115143039,
"created": 1700058639638,
"modified": 1700058639638,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"performer": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"localContent": {},
"qualifiedLinks": {},
"codes": {},
"tags": {},
"systemMetaData": {
"secretForeignKeys": [
"6b38500c-4151-4e9c-8fa4-e44de4426a11"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "wfptNWxdjhwJymY4BeXlOOT9RgTmydwW1QhRBFlSDes6k+5c5fcCMmefvxYbehdVs/QWYR4K0aPiTboeZQN41Q==",
"tags": {}
},
"notes": []
}
]
}

Filters offer you a lot of possibilities. Go have a look at the How To search data in iCure using complex filters guide to know more about them.

Get your newly created Observation information

Finally, you can also get the information of a specific observation, if you know its id.

const johnWeight = await api.observationApi.get(johnData.rows[0].id)
expect(johnData.rows[0].id).to.be.equal(johnWeight.id)
expect(johnWeight.component.numberValue).to.be.equal(92.5)
johnWeight
{
"id": "2c8af9c3-25e7-498e-a8d2-5ba6c555e305",
"identifiers": [],
"batchId": "eaeba6ed-faec-4bf2-8761-60cd28ac1745",
"healthcareElementIds": [],
"index": 0,
"component": {
"numberValue": 92.5
},
"valueDate": 20220203111034,
"openingDate": 20231115143039,
"created": 1700058639638,
"modified": 1700058639638,
"author": "17e392da-8e36-4e4e-abd7-7eef3e434395",
"performer": "3ed06450-17e5-47b6-ba4f-7a0a084df56b",
"localContent": {},
"qualifiedLinks": {},
"codes": {},
"tags": {},
"systemMetaData": {
"secretForeignKeys": [
"6b38500c-4151-4e9c-8fa4-e44de4426a11"
],
"cryptedForeignKeys": {},
"delegations": {},
"encryptionKeys": {},
"securityMetadata": {
"secureDelegations": {},
"keysEquivalences": {}
},
"encryptedSelf": "wfptNWxdjhwJymY4BeXlOOT9RgTmydwW1QhRBFlSDes6k+5c5fcCMmefvxYbehdVs/QWYR4K0aPiTboeZQN41Q==",
"tags": {}
},
"notes": []
}

To know more about the Observation information, go check the References

What's next ?

Congratulations, you are now able to use the basic functions of the iCure MedTech SDK. However, the SDK offers a lot of other services: head to the How-To section to discover all of them. You can also check the different In-Depth Explanations, to understand fully what's happening behind each service of iCure.