2026Active

kh · hospital market data

Next.js 16 · Prisma 7 · PostgreSQL · MapLibre · Zod

Overview

Hospital market intelligence for DACH + Poland/Czechia: one map, 1,448 hospitals, zero silently-broken rows.

The source of truth is a single hand-maintained 44-column CSV, nine record types discriminated by a Datensatztyp column, covering 1,448 hospital sites, 228 MVZ sites, 79 chains and 498,433 beds across five countries. The importer's job is to make sure a new version of that file lands in PostgreSQL without importing anything broken.

desktop landing scroll

How does the import stay honest?

The pipeline distinguishes hard gates from soft issues: schema violations and segment-total mismatches abort the import, while missing beds become collected ImportIssues. The segment-total gate validates the CSV against its own stated totals, and the same gate runs in CI against the shipped file.

What sits on top of the pipeline?

A Next.js register app with a MapLibre geo dashboard and market KPIs. 137 named individuals are deliberately excluded from app data, a privacy decision documented, not improvised.

Field notes

  • A hand-written ~90-line RFC-4180/BOM/CRLF CSV reader replaced a library that couldn't handle the file's realities.
  • Real-world mess, handled: postcodes turned into numbers ("01067" → 1067, fixed with per-country re-padding: DE/PL/CZ 5 digits, AT/CH 4), "Ja"/"Nein" → booleans, coordinate bounds checks (lat 45-56, lng 5-25).
  • ~1,300 idempotent upserts in one $transaction, timeout raised 5s → 120s to make it honest.
  • Originally imported Excel; CI history still shows import:excel from before the rework.
kh poster, map dashboard
1.448 krankenhäuser · 5 countries · one map
kh register table view
44-column source csv · zod-validated imports
kh market KPI view
79 chains & verbünde · 228 mvz standorte