DEV Community

Botánica Andina
Botánica Andina

Posted on

How I Built a Health Data API With Zero Dependencies (And Why You Should Too)

Every health app I've seen follows the same pattern: user enters data → data goes to server → server queries database → response comes back. But what if your health tool didn't need a server at all?

I built an herb-drug interaction checker that runs entirely in the browser. 592 interactions, autocomplete search, severity ratings — all client-side. Zero API calls. Zero backend. Zero privacy concerns.

Here's what I learned building it, and why this architecture makes sense for health tools specifically.

Why Client-Side for Health Data?

Health data is sensitive. When someone searches "metformin + St. John's Wort," they're telling you they take metformin (diabetes) and are considering St. John's Wort (depression). That's two diagnoses revealed in one query.

With a server-side API, you're collecting that data whether you want to or not. Server logs, analytics, database queries — all contain sensitive health information subject to regulations you probably don't want to deal with.

Client-side means the query never leaves the browser. No HIPAA concerns. No GDPR health data processing. No breach notification obligations. The user's health data stays on their device, period.

The Architecture

The entire database lives in a single JavaScript constant:

const INTERACTIONS = [
  {
    id: "int-001",
    substance_a: { id: "warfarin", name: "Warfarin", type: "drug" },
    substance_b: { id: "ginkgo", name: "Ginkgo biloba", type: "herb" },
    severity: "high",
    effect: "Increased bleeding risk",
    mechanism: "Ginkgo inhibits platelet-activating factor (PAF)",
    evidence: "clinical",
    sources: ["Bent et al. 2005", "WHO Monographs"]
  },
  // ... 591 more
];
Enter fullscreen mode Exit fullscreen mode

When the user types a substance name, the autocomplete searches this array with a fuzzy matcher:

function fuzzyMatch(query, items) {
  const q = query.toLowerCase().normalize("NFD")
    .replace(/[\u0300-\u036f]/g, ""); // strip accents

  return items
    .map(item => {
      const name = item.name.toLowerCase().normalize("NFD")
        .replace(/[\u0300-\u036f]/g, "");

      // Exact prefix match scores highest
      if (name.startsWith(q)) return { item, score: 100 };

      // Contains match
      if (name.includes(q)) return { item, score: 80 };

      // Word-start match (e.g., "john" matches "St. John's Wort")
      const words = name.split(/[\s.-]+/);
      if (words.some(w => w.startsWith(q))) return { item, score: 60 };

      return { item, score: 0 };
    })
    .filter(r => r.score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, 8)
    .map(r => r.item);
}
Enter fullscreen mode Exit fullscreen mode

The accent normalization is crucial for a Spanish-language tool. Users type "maca" or "máca" interchangeably — both need to match.

Performance: 592 Interactions in <1ms

You might think searching 592 records on every keystroke would be slow. It's not. JavaScript engines are ridiculously fast at iterating small arrays.

I benchmarked it:

Operation Time
Full array scan (592 items) 0.08ms
Fuzzy match + sort + slice 0.12ms
DOM update (8 suggestions) 0.3ms
Total per keystroke ~0.5ms

For context, a 200ms API round-trip would be 400x slower. And that's on a good connection. On a 3G connection in rural Peru (where many of our users are), the API approach would take 800ms+.

The threshold where you need server-side search is roughly 50,000+ records with complex queries. For a curated medical database? Client-side wins every time.

The Data Pipeline

The hardest part wasn't the code — it was the data. Building a reliable herb-drug interaction database from scratch required:

  1. PubMed search — queried 250+ herb names × "drug interaction" → 1,200 papers
  2. WHO Monographs — 92 monographs on medicinal plants, each with an interactions section
  3. Natural Medicines Comprehensive Database — gold standard for evidence ratings
  4. Manual review — every interaction cross-referenced with at least 2 sources

Each interaction gets an evidence level:

  • Clinical — randomized controlled trials or systematic reviews
  • Pharmacological — mechanism understood, case reports exist
  • Theoretical — based on known pharmacology, no human data
  • Traditional — based on traditional medicine practice only

This matters. Telling someone "Ginkgo + Warfarin = bleeding risk" backed by clinical trials is very different from "Cat's Claw might theoretically affect immunosuppressants."

Lessons for Developers Building Health Tools

1. Evidence levels are your liability shield

If you present health information without evidence context, you're implicitly telling users it's all equally reliable. It's not. Label everything.

2. Disclaimers aren't just legal boilerplate

Our disclaimer explicitly states: "This tool is for educational purposes. Always consult your healthcare provider." But more importantly, we show it before results appear, not buried in a footer. Users should know the limitations before they act on the information.

3. Offline-first is a feature, not a limitation

Health information is most needed in places with bad internet: rural clinics, pharmacies in developing countries, field research. A tool that works offline is genuinely more useful than one that requires connectivity.

// Service worker for offline support (optional but valuable)
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('health-tools-v1').then(cache =>
      cache.addAll(['/interacciones/', '/interacciones/index.html'])
    )
  );
});
Enter fullscreen mode Exit fullscreen mode

4. Embed-ready tools get organic backlinks

We added an embed feature that generates an iframe snippet:

<iframe
  src="https://botanicaandina.com/herramientas/interacciones/?embed=true"
  width="100%" height="600" frameborder="0"
  title="Herb-Drug Interaction Checker">
</iframe>
Enter fullscreen mode Exit fullscreen mode

Health blogs and university sites can embed the tool directly. Each embed is a backlink. This is how open-source health tools grow organically.

5. Internationalization from day one

Our tool supports Spanish and English. The data model stores both:

{
  effect: "Increased bleeding risk",
  effect_es: "Mayor riesgo de sangrado",
}
Enter fullscreen mode Exit fullscreen mode

For LATAM health tools, Spanish-first with English fallback serves 95% of users. But don't auto-translate medical terms — "anticoagulante" and "blood thinner" aren't perfectly equivalent in clinical context.

What I'd Do Differently

More structured data from the start. I initially stored interactions as flat text. Switching to structured objects (substance IDs, severity enums, evidence enums) made the tool 10x more powerful but required a painful migration.

Test with real pharmacists earlier. A pharmacist found 3 severity misclassifications in the first week. Domain experts catch things that unit tests never will.

Schema.org markup. Adding WebApplication + FAQPage structured data increased our visibility in Google rich results. Do this from the start, not as an afterthought.

The Stack

  • HTML/CSS/JS — single file, no build tools
  • No frameworks — vanilla JS is fast enough for this scale
  • No backend — all data embedded in the page
  • Hosting — GitHub Pages (free, fast, global CDN)
  • Total size — 127KB gzipped (entire app + 592 interactions)

The entire tool is one HTML file. You can save it to your desktop and it works.

Try It

The Herb-Drug Interaction Checker is live and free. Search any supplement or herb name to see known interactions with medications.

If you're building health tools, consider whether your data really needs a backend. For curated, reference-style databases (drug interactions, dosing calculators, symptom checkers), client-side architecture gives you better performance, zero privacy concerns, and offline capability — at the cost of a larger initial page load.

The tradeoff is worth it.


I'm building open-source health tools for the Latin American market at Botanica Andina. If you're interested in health data, NLP for medical Spanish, or client-side tool architecture, let's connect.

Top comments (0)