Building a Basic Healthcare Records DApp on Stacks

Gbolahan AkandeGbolahan Akande
5 min read

Solving medical record chaos with blockchain — one patient at a time — A Real-World Patient Story

Introduction

I’ve always wanted a way for patients to avoid retaking the same medical tests just because they’ve moved or switched hospitals. What if I lost my health records notebook? What if I couldn’t recall my treatment history during an emergency?
That’s been my story — moving around, visiting hospitals repeatedly, and facing the same diagnosis process each time simply because my data wasn’t accessible. So I imagined a world where my health record could follow me, securely, digitally, and with my control over who sees it and when.
That vision led to HealthEncrypted — a secure, decentralized application built on the Stacks blockchain, using Clarity smart contracts and a modern React + TypeScript + Vite frontend.
Let me show you how it can be built, what I learned, and how you can try doing the same.

Problem-Solution Thinking

Problem: Patients can’t easily access their health records across multiple providers.
Solution: Build a blockchain-powered DApp that allows patients to:

  • Register and store encrypted health data

  • Give hospitals permissioned access

  • Update and retrieve records securely

Tech Stack

  • Smart Contract: Clarity on Stacks

  • Frontend: React + TypeScript + Vite

  • Wallet: Hiro Wallet for authentication


Step 1: Smart Contract Logic — PatientRecord.clar

This is where the data structure and access control begin. We define a map called patient-records to track core patient data

(define-map patients ((id (string-utf8 30)))
    ((name (string-utf8 50)) (dob uint) (blood-type (string-utf8 5))))

We also track health records, but rather than store full data on-chain (privacy risk!), we store an encrypted hash or reference:

(define-map records ((id (string-utf8 30))) ((record-hash (string-utf8 100))))

Here's how patients are registered:

(define-public (register-patient (id (string-utf8 30)) (name (string-utf8 50)) (dob uint) (blood-type (string-utf8 5)))
  (begin
    (map-set patients ((id id)) ((name name) (dob dob) (blood-type blood-type)))
    (ok true)))

...and updated:

(define-public (update-patient-record (id (string-utf8 30)) (record-hash (string-utf8 100)))
  (begin
    (map-set records ((id id)) ((record-hash record-hash)))
    (ok true)))

What I learned:

  • String limits are strict. Plan your field lengths carefully.

  • Map access is explicit. No shortcuts; Clarity makes you think through every data call.

Step 2: Connecting to the Smart Contract — contract.tsx

In secure-health-frontend/src/utils/contract.tsx, I created async functions to interact with Clarity:

export async function registerPatient(...) => {
  contractAddress: 'ST1...'
  functionName: 'register-patient',
  functionArgs: [ stringUtf8CV(id), stringUtf8CV(name), uintCV(dob), stringUtf8CV(bloodType) ]
}

Same for fetching and updating records. Each function builds a contract call payload that can be used by @stacks/connect to trigger wallet interaction.

What I learned:

  • It’s best to keep all contract logic in one utility file.

  • Always handle errors from wallet or network issues.

Step 3: Connecting Wallet — ConnectWallet.tsx

Authentication is handled using Hiro Wallet, managed in AuthContext and triggered via:

<button onClick={handleConnect}>Connect Wallet</button>

Once authenticated, users can access their dashboard securely.

Step 4: Frontend Flow — Dashboard.tsx and Home.tsx

Home.tsx serves as the landing page. If the user is authenticated, they’re redirected to the dashboard:

{isAuthenticated ? 'Go to Dashboard' : 'Get Started'}

The Dashboard.tsx component fetches patient records and renders them beautifully:

useEffect(() => {
  if (!isAuthenticated) return;

  const fetchRecords = async () => {
    const response = await getPatientRecord('demo-patient-id');
    const result = await openContractCall(response); // using @stacks/connect
    setRecords(result); // assuming a transformation step
  };

  fetchRecords();
}, [isAuthenticated]);

This interaction leverages the getPatientRecord() function from contract.tsx, which builds the contract call like this:

export async function getPatientRecord(patientId: string) {
  const options: ContractCallOptions = {
    contractAddress: CONTRACT_ADDRESS,
    contractName: CONTRACT_NAME,
    functionName: 'get-patient-record',
    functionArgs: [stringUtf8CV(patientId)],
    network,
  };

  return options;
}

What I learned:

  • Designing a pleasant UI matters. I used TailwindCSS to keep things elegant.

  • State and auth flows must be tightly scoped to avoid leaking patient context.

  • Abstracting contract logic into contract.tsx made things more maintainable and testable.

Home.tsx serves as the landing page. If the user is authenticated, they’re redirected to the dashboard:

{isAuthenticated ? 'Go to Dashboard' : 'Get Started'}

The Dashboard.tsx component fetches patient records and renders them beautifully:

useEffect(() => {
  // placeholder — mock fetch, to be replaced with real contract call
  setRecords([...])
}, [])

What I learned:

  • Designing a pleasant UI matters. I used TailwindCSS to keep things elegant.

  • State and auth flows must be tightly scoped to avoid leaking patient context.


Lessons Learned

  • Start with the pain point. My idea came from real-world frustration, not code.

  • Build first, polish later. I mocked records before wiring up the contract fully.

  • Separate logic and UI. Contracts, auth, and UI are all in their own layers.


Final Thoughts

HealthEncrypted is far from complete. But the bones are strong. With secure on-chain references, wallet-authenticated access, and clean separation of logic, the DApp can scale to more complex health data workflows.

If you’ve ever thought, "Why can’t my records just follow me?" — this tutorial shows you how to begin answering that question using blockchain.

You can view the full code on GitHub.

Feel free to fork it, remix it, or contribute.

If you're working on healthcare, data privacy, or even something completely different but on Stacks — let's connect. I’d love to hear how others are solving real-world problems with Web3 tech.

Thanks for reading — and don’t forget to get your health data encrypted! 🧬🔐

10
Subscribe to my newsletter

Read articles from Gbolahan Akande directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gbolahan Akande
Gbolahan Akande