This repository contains a Hyperledger Fabric chaincode (smart contract) implemented in Node.js for managing patients and their medical records.
- Entry point:
index.js(exports the contract) - Contract implementation:
lib/medicalContract.js
The MedicalContract provides basic CRUD-style transactions for:
- Patient registration
- Adding and updating medical records
- Querying patients and medical records
- Retrieving key history
Data is stored as simple JSON documents in the Fabric world state with docType used for rich queries:
- Patients:
{ patientId, name, nationalId, dateOfBirth, docType: 'patient' } - Medical Records:
{ recordId, patientId, doctorId, diagnosis, treatment, timestamp, docType: 'medicalRecord' }
Rich queries (CouchDB) are used to list all patients or all medical records by docType.
index.js– Exports theMedicalContractfor Fabric chaincode runtimelib/medicalContract.js– Contract logicpackage.json– Dependencies and scripts
Class: MedicalContract (extends fabric-contract-api.Contract)
Transactions:
-
initLedger(ctx)
- Current behavior: Only logs
Ledger initializedto the chaincode logger; it does not write any data. - Notes:
- This is safe to invoke multiple times (idempotent) as it does not mutate state.
- Typical usage is to preload sample data to the ledger; see the "Enhancements" section below for guidance.
- Current behavior: Only logs
-
CreatePatient(ctx, patientId, name, nationalId, dateOfBirth)
- Creates a patient document with
docType: 'patient'. - Fails if a patient with the same
patientIdalready exists. - Returns the created patient JSON string.
- Creates a patient document with
-
AddMedicalRecord(ctx, recordId, patientId, doctorId, diagnosis, treatment)
- Requires an existing patient (
patientId). - Creates a medical record with
docType: 'medicalRecord'and a timestamp derived from the transaction timestamp (ctx.stub.getTxTimestamp()), serialized as ISO string. - Returns the created record JSON string.
- Requires an existing patient (
-
UpdateMedicalRecord(ctx, recordId, newDiagnosis, newTreatment)
- Updates an existing medical record’s
diagnosis,treatment, andtimestamp. - Fails if the record does not exist.
- Returns the updated record JSON string.
- Updates an existing medical record’s
-
GetPatientHistory(ctx, key)
- Streams history for the specified key and returns an array of
{ txId, timestamp, isDelete, value }. - Known issue: The current implementation returns from inside the iteration loop, which will prematurely exit after the first iteration. Consider fixing by moving the
return JSON.stringify(history)statement after the loop terminates.
- Streams history for the specified key and returns an array of
-
QueryAllPatients(ctx)
- Rich query by
docType: 'patient'. Requires CouchDB state database. - Returns an array of patient objects.
- Rich query by
-
QueryAllMedicalRecords(ctx)
- Rich query by
docType: 'medicalRecord'. Requires CouchDB state database. - Returns an array of medical record objects.
- Rich query by
-
initLedger
- Currently no-op other than logging. This is acceptable for bootstrap but does not populate any sample data.
- Safe to call repeatedly.
-
General observations in
lib/medicalContract.js- Uses
ctx.stub.getTxTimestamp()for timestamps, which is suitable for deterministic state changes across endorsers. - Rich queries rely on
docType. For production performance, consider adding CouchDB indexes inMETA-INF/statedb/couchdb/indexes. - Minor:
const crypto = require('crypto');is imported but not used and can be removed. - Bug:
GetPatientHistoryreturns within thewhile (true)loop and will likely only produce a partial result. Move thereturnoutside the loop.
- Uses
- Node.js LTS and npm
- Hyperledger Fabric v2.x peer/orderer network with CouchDB for rich queries
- The Fabric Node.js contract runtime (
fabric-contract-api,fabric-shim) – already declared inpackage.json
npm installThis project includes a start script that launches the chaincode server and connects to a peer.
Script (from package.json):
"start": "fabric-chaincode-node start --peer.address localhost:7051 chaincode-id-name \"medical-contract:1.0.0\""Notes:
- Ensure your peer is reachable at
localhost:7051or adjust the address accordingly. - The
chaincode-id-namemust match the name used during chaincode definition in the Fabric lifecycle (e.g.,medical-contract:1.0.0). - Configure TLS flags and environment variables as required by your network.
Start the chaincode server:
npm startThen, use Fabric lifecycle to approve/commit the chaincode definition and point it to the external service according to your network’s setup.
Assuming:
- Channel:
mychannel - Chaincode name:
medical-contract - JSON args use the Fabric v2 syntax
Initialize (does not write data):
peer chaincode invoke -C mychannel -n medical-contract -c '{"Args":["initLedger"]}'Create a patient:
peer chaincode invoke -C mychannel -n medical-contract -c '{"Args":["CreatePatient","p1","Alice Smith","NAT-12345","1990-01-01"]}'Add a medical record:
peer chaincode invoke -C mychannel -n medical-contract -c '{"Args":["AddMedicalRecord","r1","p1","dr-001","Flu","Rest and hydration"]}'Update a medical record:
peer chaincode invoke -C mychannel -n medical-contract -c '{"Args":["UpdateMedicalRecord","r1","Seasonal flu","Antivirals"]}'Query all patients (CouchDB rich query):
peer chaincode query -C mychannel -n medical-contract -c '{"Args":["QueryAllPatients"]}'Query all medical records (CouchDB rich query):
peer chaincode query -C mychannel -n medical-contract -c '{"Args":["QueryAllMedicalRecords"]}'Get history for a patient key:
peer chaincode query -C mychannel -n medical-contract -c '{"Args":["GetPatientHistory","p1"]}'Adjust for TLS, MSP, and environment variables as required by your deployment.