All articles
Engineering

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.

ClinikAPI TeamApril 18, 202610 min read
How to Build an EHR in 5 Minutes (Mini EHR with ClinikAPI)

"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

ClinikAPI gives you a FHIR data layer and pre-built clinical components — the whole backbone of an EHR. Free sandbox, start in seconds.
Start Building Free

The three parts of an EHR

Strip an EHR to its core and you find this:

PartWhat it isHow we build it
Patient listRegister and find patientsclinik.patients.create / .search
ChartOne view of a patient's record<PatientDashboard />
ActionsVitals, notes, scheduling, prescribingVitalsWidget, 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 })
  }
}
Caution

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.

Note

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:

FileWhat it does
lib/clinik.tsThe server-side SDK client
app/api/clinik/route.tsThe entire backend (one proxy route)
Patient list pageRegister + find patients
app/chart/[id]/page.tsxThe chart (PatientDashboard)
Chart actionsVitals, 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: PatientDashboard renders 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 / .search is your patient list, stored as FHIR.
  • PatientDashboard renders 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


Share

Keep reading