Clinician-facing records platform
Role-aware interfaces for a patient records product, where what a user can see depends on their relationship to the record and every read is auditable.
- Role
- Front end and API
- Period
- 2024 to 2025
- Stack
- React
- Next.js
- Node.js
- PostgreSQL
- TypeScript
This is a seeded example project, not client work. It is here so the structure of a case study is right from day one, and it is replaced before this site goes live.

Measured
Enforced server-side
Access rules
Every query scoped in the repository layer, not in the view
Every record read
Audit coverage
Append-only log written in the same transaction as the read
All authorization logic
Test coverage
Unit tests on the permission resolver, run in CI
Problem
Clinicians needed a fast view of a patient record, but not every clinician was allowed to see every field, and the rules depended on the viewer's relationship to the patient rather than on a flat role.
The existing prototype resolved permissions in the interface, which meant the API would return data the screen then chose to hide. Anyone reading the network tab could see everything.
Approach
Permission resolution moved to the server and into the data layer. The API cannot return a field the viewer is not entitled to, so the interface never has to be trusted to hide anything.
The rules were written down and turned into unit tests before the screens were built, so the edge cases were argued about while they were still cheap to change.
- Authorization resolved in the repository layer, never in a component
- Rules captured as tests before any interface work
- Deny by default: a new field is invisible until a rule grants it
Architecture
Next.js front end against a Node API over Postgres. Every query is scoped by the viewer at the point of access, so there is no code path that can forget.
Reads and writes to a record append to an audit table inside the same transaction, which means a successful read cannot exist without its log entry.
- Postgres row scoping applied in one place
- Audit writes share the transaction with the operation they record
- Personal data kept out of logs and error reports
Outcome
The team could answer who had seen a record and when, which they could not do before.
Adding a new field became a two-line rule change plus a test, rather than an audit of every screen that might display it.