Building a FHIR Adapter from Scratch: Why Rust, Why Now
Series: Building a Multi-Tenant FHIR Adapter 1/5
The Problem: Healthcare Data in Silos
Healthcare systems are a mess. I don't mean that lightly—after years of working with legacy hospital databases, I've seen it all: patient data stored in decades-old MySQL schemas (or even Microsoft Access), custom field names that made sense to someone in 1998, and zero interoperability between systems.
When a doctor needs to access a patient's complete medical history, they often can't. The data exists, scattered across multiple incompatible systems, each speaking its own dialect.

FHIR (Fast Healthcare Interoperability Resources) was supposed to solve this. It's a modern standard for exchanging healthcare information electronically. But there's a catch: most healthcare providers run on legacy systems that predate FHIR by decades.
The Solution: A Bridge Between Worlds
I decided to build a FHIR adapter—a system that sits between legacy databases and modern FHIR-based applications. It translates old-school database schemas into standard FHIR resources on the fly.
But here's where it gets interesting: this isn't just for one hospital. It's multi-tenant, meaning hundreds of healthcare organizations can use the same adapter instance, each with their own completely isolated data and custom database schemas.

Why Rust?
I'll be honest: I started from zero with Rust. No production experience, just curiosity and a belief that Rust was the right tool for this job.
Here's why I chose it:
1. Performance Matters in Healthcare
When a doctor requests a patient's records, they need them now. Not in 5 seconds, not after a loading spinner—now. Rust's zero-cost abstractions and performance characteristics meant I could build a system that handles thousands of requests per second without breaking a sweat.
2. Memory Safety Without Garbage Collection
Healthcare data is sensitive. A memory leak or buffer overflow isn't just a bug—it's a potential HIPAA violation. Rust's borrow checker guarantees memory safety at compile time, eliminating entire classes of vulnerabilities that plague C/C++ systems.
3. Fearless Concurrency
A multi-tenant system means handling hundreds of database connections simultaneously, each to different legacy databases. Rust's ownership model makes concurrent programming not just possible, but safe and predictable.
// This is legal Rust - the compiler guarantees safety
async fn handle_request(tenant_id: &str) -> Result<Patient> {
let pool = tenant_pool_manager.get_pool(tenant_id).await?;
let config = config_resolver.get_config(tenant_id).await?;
// Multiple async operations, all safe
let patient = fetch_patient(&pool, &config).await?;
Ok(patient)
}
4. Learning Curve as a Feature
Yes, Rust is hard. The borrow checker will fight you. You'll spend hours debugging lifetime errors. But here's the thing: every error you fix at compile time is a bug that won't crash in production.

The Tech Stack
After evaluating options, here's what I landed on:
- Backend: Rust + Axum (web framework)
- Database: MySQL (for adapter config) + dynamic connections to tenant databases
- Authentication: Keycloak (enterprise SSO)
- Admin Panel: Next.js 14 + TypeScript
- Deployment: Docker + systemd
Early Decisions That Mattered
Decision 1: Dynamic Configuration Over Code Generation
Instead of generating code for each tenant's schema, I built a dynamic mapping engine that reads configuration at runtime. This means:
- ✅ No recompilation needed when onboarding a new tenant
- ✅ Tenant admins can configure mappings via UI
- ❌ More complex runtime logic
Decision 2: Compile-Time Safety Where Possible, Runtime Flexibility Where Needed
I used Rust's type system aggressively for the core adapter logic, but accepted serde_json::Value for the dynamic mapping layer. The tradeoff was worth it.
Decision 3: Admin Panel in TypeScript, Not Rust
I could have built the admin UI in Rust (Yew, Leptos, etc.), but Next.js was the pragmatic choice. Development velocity matters, and React's ecosystem is unmatched for building complex forms.
What's Coming Next
In the next posts, I'll dive deeper into:
- Part 2: Multi-tenant architecture and database isolation
- Part 3: The dynamic mapping engine that powers it all
- Part 4: Security, authentication, and audit logging
- Part 5: Building an admin panel that doesn't suck
Starting from Zero
When I started this project, I had:
- Zero production Rust experience
- A vague understanding of FHIR
- A belief that healthcare IT could be better
Six months later, I have:
- A working multi-tenant FHIR adapter
- 20,000+ lines of Rust
- Battle scars from fighting the borrow checker (and winning)

If you're considering Rust for a production project, here's my advice: do it. The learning curve is steep, but the payoff is real. You'll write better code, catch bugs earlier, and sleep better knowing your production system won't randomly segfault at 3 AM.
This is Part 1 of a 5-part series documenting my journey building a multi-tenant FHIR adapter from scratch. Follow along at compile.care for updates.