How to Build an EHR in 5 Minutes (Mini EHR with ClinikAPI)
A deep, hands-on tutorial: build a working mini EHR — register patients, open a chart, record vitals, write notes, schedule visits, and prescribe — using ClinikAPI's FHIR API and React components. Full code.
"Build an EHR in five minutes" sounds like a joke — EHRs are famously massive. But here's the secret: an EHR is really just three things — a patient list, a chart, and a few clinical actions. The reason they take years is the infrastructure underneath: a compliant database, FHIR modeling, a charting UI, search, audit logging. If a platform hands you all of that as an API plus pre-built components, the EHR itself assembles in minutes. This is a deep, hands-on build of a working mini EHR, with real code.
The platform we build on is ClinikAPI — a FHIR-native API with a React component library. Why we recommend it up front:
- Free to start: Get your API keys in seconds — no credit card needed.
- The data layer: Storage, validation, search, and compliance as an API.
- The screens: 14 pre-built clinical components, including the whole chart.
- FHIR-native: Your EHR's data is standard and interoperable.
- Compliant: HIPAA-compliant, SOC 2-audited, with a signed BAA.
Quick Answer
An EHR is three things — a patient list, a chart, and clinical actions — and with ClinikAPI you build all three in minutes. You register patients with clinik.patients.create and find them with clinik.patients.search. You render the chart with the PatientDashboard component, which shows encounters, vitals, medications, labs, notes, and prescriptions from FHIR data. You add actions with components like AppointmentScheduler, VitalsWidget, NoteEditor, and PrescriptionForm, each storing data as FHIR through your backend proxy route. ClinikAPI handles the database, validation, search, encryption, audit logging, and compliance; you assemble the app. The result is a working mini EHR — and the only boilerplate you write is a single proxy route.
Get your free API keys
The three parts of an EHR
Strip an EHR to its core and you find this:
| Part | What it is | How we build it |
|---|---|---|
| Patient list | Register and find patients | clinik.patients.create / .search |
| Chart | One view of a patient's record | <PatientDashboard /> |
| Actions | Vitals, notes, scheduling, prescribing | VitalsWidget, NoteEditor, AppointmentScheduler, PrescriptionForm |
Build those three and you have a real EHR core. Let's do it.
Step 1: Install and set up the SDK
npm install @clinikapi/sdk @clinikapi/react
// lib/clinik.ts — server-only
import { Clinik } from '@clinikapi/sdk'
export const clinik = new Clinik(process.env.CLINIKAPI_SECRET_KEY!, {
baseUrl: 'https://api.clinikapi.com', // default
timeout: 30_000,
retries: 2,
})
Your clk_live_… key stays server-side. Everything the components do flows through one backend route.
Step 2: The backend proxy (your whole API in one route)
Every component POSTs { action, data } to this route; you route each action to the SDK. This single file is the entire backend of your mini EHR:
// app/api/clinik/route.ts — server-only
import { clinik } from '@/lib/clinik'
export async function POST(req: Request) {
const { action, data } = await req.json()
// Authenticate + authorize the user for data.id / data.patientId first.
try {
switch (action) {
case 'patients.read': return Response.json(await clinik.patients.read(data.id, { include: data.include }))
case 'patients.create': return Response.json(await clinik.patients.create(data))
case 'appointments.create': return Response.json(await clinik.appointments.create(data))
case 'observations.create': return Response.json(await clinik.observations.create(data))
case 'notes.create': return Response.json(await clinik.notes.create(data))
case 'prescriptions.create': return Response.json(await clinik.prescriptions.create(data))
default: return Response.json({ error: 'Unknown action' }, { status: 400 })
}
} catch {
return Response.json({ error: 'Request failed' }, { status: 500 })
}
}
This route is where your auth lives. ClinikAPI handles storage, encryption, and audit logging — but confirming the signed-in user may access a given patient is your job, right here, before each SDK call.
Step 3: Register and find patients
The patient list is two SDK calls. Register a patient as a FHIR Patient:
const { data: patient } = await clinik.patients.create({
firstName: 'Jane',
lastName: 'Doe',
email: '[email protected]',
gender: 'female',
birthDate: '1990-03-15',
address: { line: ['123 Main St'], city: 'Austin', state: 'TX', postalCode: '78701', country: 'US' },
})
And find patients with search:
const { data: patients } = await clinik.patients.search({ name: 'doe' })
// render a list; each row links to /chart/[patient.id]
That's your patient list — registration and lookup, stored as FHIR.
Step 4: Build the chart
Here's where minutes-not-months becomes obvious. The chart — the screen that takes the longest in any EHR — is one component:
// app/chart/[id]/page.tsx
'use client'
import { PatientDashboard } from '@clinikapi/react'
export default function Chart({ params }: { params: { id: string } }) {
return (
<PatientDashboard
proxyUrl="/api/clinik"
patientId={params.id}
theme="light"
/>
)
}
PatientDashboard renders the patient's encounters, vitals, medications, labs, notes, and prescriptions — the full chart, from FHIR data, with no charting code on your side.
Step 5: Add clinical actions
An EHR isn't read-only — clinicians record vitals, write notes, schedule, and prescribe. Each is a component you place around the chart:
import {
AppointmentScheduler, VitalsWidget, NoteEditor, PrescriptionForm,
} from '@clinikapi/react'
function ChartActions({ patientId }: { patientId: string }) {
return (
<>
<AppointmentScheduler proxyUrl="/api/clinik" patientId={patientId} theme="light" />
<VitalsWidget proxyUrl="/api/clinik" patientId={patientId} theme="light" />
<NoteEditor proxyUrl="/api/clinik" patientId={patientId} theme="light" />
<PrescriptionForm proxyUrl="/api/clinik" patientId={patientId} theme="light" />
</>
)
}
Each one stores its data as FHIR through the proxy route you already built — and it all shows up in the PatientDashboard, because they share one FHIR record.
Want to see it before wiring the backend? Set proxyUrl="demo" on any component to run the whole UI with mock data — a clickable mini EHR with zero backend.
What you just built
In five files you have a working mini EHR:
| File | What it does |
|---|---|
lib/clinik.ts | The server-side SDK client |
app/api/clinik/route.ts | The entire backend (one proxy route) |
| Patient list page | Register + find patients |
app/chart/[id]/page.tsx | The chart (PatientDashboard) |
| Chart actions | Vitals, notes, scheduling, prescribing |
Register a patient, open their chart, record a vital, write a note, book a visit, prescribe — a real EHR core, assembled instead of built.
Product Insight: Why ClinikAPI Makes This Possible
EHRs take years because of the infrastructure, not the screens. ClinikAPI hands you the infrastructure as an API and the screens as components — so the EHR itself is fast.
- The data layer:
patients,observations,appointments,notes,prescriptions, and 50+ more FHIR resources, stored, validated, and searchable. - The chart:
PatientDashboardrenders the whole record. - The actions: scheduler, vitals, notes, prescriptions, and more, as drop-in components.
- One backend route: the action-router proxy is your entire API surface.
- Compliance: HIPAA-compliant, SOC 2-audited, with a signed BAA.
You assemble the EHR; ClinikAPI is the backbone. See it all in the UI Library and API-First EHR.
Frequently Asked Questions
1. Can you really build an EHR in 5 minutes?
A working mini EHR, yes — because the data layer and screens are pre-built. You assemble patients, a chart, and clinical actions instead of building a database, charting, and compliance.
2. What are the core parts of an EHR?
A patient list, a chart, and clinical actions — in FHIR terms, Patients, a PatientDashboard, and components that create Observations, notes, Appointments, and prescriptions.
3. How do I store patient data?
Use clinik.patients.create for the patient and the other SDK resources for clinical data — all stored as FHIR on HIPAA-compliant infrastructure.
4. Do I build the chart UI myself?
No — PatientDashboard renders the whole chart from FHIR data. You add action components around it.
5. Is a mini EHR HIPAA-compliant?
The data layer is — ClinikAPI handles storage, encryption, audit logging, and signs a BAA. You handle your own app's auth and authorization.
Conclusion
EHRs feel impossibly large because of everything underneath the screens — the compliant database, the FHIR modeling, the audit logging, the charting. Hand all of that to a FHIR API and a component library, and what's left is small: a patient list, a chart, and a few actions. With ClinikAPI you write one proxy route, drop in PatientDashboard and a handful of components, and you have a real, working mini EHR. From there, it's just your product on top.
Key takeaways:
- An EHR is three parts: a patient list, a chart, and clinical actions.
clinik.patients.create/.searchis your patient list, stored as FHIR.PatientDashboardrenders the entire chart with no charting code.- One action-router proxy route is your whole backend.
- ClinikAPI handles storage and compliance; you handle your app's auth.
Ready to build? Get your free API keys or explore the UI Library.
Related Articles
- API-First EHR: Build On a Healthcare API
- How to Build an AI Note Taker in 5 Minutes
- Healthcare React Component Library: The Complete Guide
- How to Build a Patient Dashboard in React
- How to Build a Telehealth MVP Fast