Back to BlogTroubleshooting

HealthTech App Slow at Clinic Peak Hours? Diagnose and Fix It

Rupak Amin

Founder & Lead Engineer, RAITHub

8 min read

RAITHub ships and tests production software. See QA as a Service or talk to us.

A HealthTech app that slows at clinic peak hours almost always has a slow database path, not a slow server. The usual causes, in order: a missing index on a patient or appointment query, an N+1 pattern loading records one by one, a connection-pool ceiling under concurrent staff, and a dashboard that recomputes on every load. Find which one with query logs before you change anything.

This is general engineering information, not compliance advice; keep production and patient data in your own covered cloud and confirm obligations with your adviser. If you would rather have it fixed for you, see how RAITHub would fix this below.

Why does my healthcare app slow down at 9am but not overnight?

Peak-hour slowness is a concurrency and data-volume problem. Overnight, one request runs against a small working set; at clinic open, dozens of clinicians, front-desk staff and the patient portal hit the same tables at once. A query that takes 40ms alone can take 4 seconds when fifty of them queue on one slow index scan or wait for a free database connection. The server CPU looks idle because the time is spent waiting, not computing.

SymptomMost likely causeFirst check
Appointment list takes seconds to loadMissing index on the date or clinician columnRun EXPLAIN on the query
Patient page slow, gets slower with more recordsN+1: one query per related rowCount queries per request in logs
App freezes under many staff, then recoversConnection-pool ceilingPool size vs concurrent users
Reports or dashboards slow for everyoneUncached aggregate recomputed per loadIs the result cached at all?
Slow only on serverless after idleCold starts and new connectionsLatency of the first request

How do I find the cause in an hour?

Measure before you guess. Add a health-check route that reports database latency and a sample query time, turn on slow-query logging, and count queries per request. A minimal health route in a Next.js API handler:

// app/api/health/route.ts
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'

export async function GET() {
  const start = Date.now()
  // A cheap round-trip proves the connection and pool are healthy.
  await prisma.$queryRaw`SELECT 1`
  const dbMs = Date.now() - start

  const qStart = Date.now()
  // Swap in the query your clinic list actually runs.
  const sample = await prisma.appointment.count({
    where: { startsAt: { gte: new Date(new Date().setHours(0, 0, 0, 0)) } },
  })
  const queryMs = Date.now() - qStart

  return NextResponse.json({ ok: dbMs < 100, dbMs, queryMs, sample })
}

If dbMs climbs under load, you are pool-starved. If queryMs is high even alone, the query needs an index or a rewrite. Confirm with the database's own plan:

EXPLAIN ANALYZE
SELECT id, patient_id, starts_at
FROM appointments
WHERE clinician_id = $1
  AND starts_at >= date_trunc('day', now());
-- A "Seq Scan" on a large table is the signal to add an index.

The full Next.js version of this diagnosis is in why is my Next.js app so slow in production; the indexing detail is in the PostgreSQL indexing guide. If the slow screen is a patient-facing portal, the build patterns are in building a patient portal SaaS.

What are the fixes, in order?

Apply the lowest-cost high-impact fix first, retest, then move on. Do not add five changes at once; you will not know which one helped.

  1. Add the missing index. A composite index on the columns a hot query filters and sorts by, for example (clinician_id, starts_at), often turns a seconds-long scan into milliseconds. Build it concurrently so it does not lock the table.
  2. Kill the N+1. Load related records in one query (a join or an include) instead of one query per row. A patient page that fired 1 + 50 queries becomes 2.
  3. Size the connection pool. Serverless functions each open connections; a pooler (PgBouncer, or Neon's and Supabase's built-in poolers) keeps the database from hitting its connection limit at peak.
  4. Cache the expensive aggregate. A dashboard count that is fine a few seconds stale can be cached, so a hundred staff loads do not each run the same heavy query.
  5. Move slow work off the request. Reminders, report generation and syncs belong in a background job, not in the page the clinician is waiting on.
-- Build the index without locking the live table.
CREATE INDEX CONCURRENTLY idx_appt_clinician_day
  ON appointments (clinician_id, starts_at);

Buy, build or hire the fix?

OptionChoose this whenTrade-off
Scale the server or database tier upYou need breathing room today and the bill is acceptableHides the real cause; a missing index stays missing and costs grow
A no-code or vendor platformYou have outgrown spreadsheets and do not want to run infrastructureYou cannot tune the queries; peak-load behaviour is the vendor's to fix
Fix it yourself with EXPLAIN and logsA developer can read query plans and owns the codebaseLowest cost; needs someone who can tell a plan problem from a pool problem
A code rescue or performance passYou want it diagnosed and fixed with tests so it stays fixedAn outside dependency; scope the regression tests into the work

Doing it yourself is realistic: plan 4 to 8 hours if you can read a query plan and run migrations safely. The main risk of going alone is adding an index that does not match the query, or caching data that must always be fresh, such as a current medication list.

Why RAITHub for this

  • Database performance is everyday work. PropDesk, a property-management SaaS RAITHub built, runs 1,024 tests across 130+ endpoints with 5 daily automation jobs that keep slow work off the request path.
  • Fixes land behind tests. Each change ships with a regression test, so the slow path does not come back on the next release, as covered in every release breaks something.
  • Honest limits. This is performance engineering, not a compliance exercise. RAITHub has built a healthcare scheduling app for a client but has not shipped a regulated health product, and it holds no SOC 2 or ISO 27001 certification. Production and patient data stay in your own covered cloud; development uses synthetic data.

When you don't need us

  • The slowness is one missing index your own developer can add in an afternoon.
  • You are pre-launch with a handful of users; profile first, optimise when real load appears.
  • You already have an engineer who reads query plans and owns performance budgets.

How RAITHub would fix this

  • Scope: a free 15-minute call on the symptoms, the peak pattern and the stack; you confirm you are authorised to have the app diagnosed.
  • Diagnose: add a health-check route, enable slow-query logging, and rank the hot paths by their cost under real concurrency.
  • Fix: indexes, N+1 removals, pooling and caching, applied one at a time against synthetic data and retested under load.
  • Deliver: the fixes behind regression and load tests in your repository, with a short runbook for the next spike.
  • What you receive: tested fixes, CI gates, a handover note, and full IP; an NDA is standard. Timeline for a focused performance pass is typically within a 2 to 4 week code rescue.

The work is fixed-price, quoted in writing after the call. See SaaS development, the HealthTech industry page, and the sibling guide on scaling from one clinic to many. To book it, talk to RAITHub about your HealthTech app.

General engineering information only; confirm compliance obligations with your adviser. Documentation checked on 11 October 2026.

Frequently asked questions

Why is my healthcare app slow only during clinic hours?

Because peak hours add concurrency and data volume at once. Many staff and the patient portal hit the same tables together, so a query that is fast alone queues behind others on a slow index scan or waits for a free database connection. Overnight the same query runs against a small, uncontested working set and feels fine.

How do I know if the problem is the database or the server?

Add a health-check route that reports database round-trip latency and a sample query time. If the server CPU is idle while requests are slow, the time is spent waiting on the database or the connection pool, not computing. Then run EXPLAIN ANALYZE on the slow query to see whether it scans a table that should be indexed.

What is an N+1 query and why does it slow a patient page?

An N+1 pattern runs one query to load a list, then one more query per item to load its related data, so a page with fifty appointments fires fifty-one queries. Under load those multiply across users. Loading the related rows in a single join or include collapses it to two queries.

Will adding more server capacity fix it?

Usually only for a while. Scaling up hides a missing index or an N+1 pattern behind raw power, so the bill grows while the real cause stays. Fix the query path first; scale when the work itself genuinely needs more capacity.

Is it safe to tune a live healthcare database?

Build indexes concurrently so they do not lock the live table, test each change against synthetic data first, and keep production and patient data in your own covered cloud. Treat performance changes like any other release: behind a test, with a rollback plan. This is engineering guidance; confirm data-handling obligations with your adviser.

healthtech performancehealthcare app slowdatabase indexingn+1 queriesconnection poolingclinic peak load

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.