# Build a longitudinal patient record with TypeScript.

A timeline is not a giant sorted array. It is a source-aware projection over records with different clinical dates, update times, identities, and completeness.

[Production-grade access is free with an account.](https://finchnode.com/signup) The $0/month plan includes 100 connected patient-months, 100,000 production API calls per month, one production application, and an unlimited synthetic sandbox.

- Author: [FinchNode Engineering](https://finchnode.com/authors/finchnode-engineering)

- Published: 2026-08-25

- Last reviewed: 2026-08-25

- Canonical URL: https://finchnode.com/blog/longitudinal-patient-record-typescript

- Evidence boundary: Uses synthetic examples and source-aware record practices; it does not provide clinical decision support.

## Bottom line

Ingest records per source and category, retain stable source identities, map them to typed timeline events, and rebuild projections deterministically. Clinical time, source update time, and ingestion time should remain separate fields.

## Key takeaways

- Use a stable source-aware key so refreshes update existing events instead of duplicating them.
- Sort by clinical time for display while retaining update and ingestion timestamps for operations.
- Treat uncertain dates and missing provenance as visible data-quality states.

## Define one timeline event without flattening the record

```typescript
type TimelineEvent = {
  key: string;
  kind: 'lab' | 'condition' | 'medication' | 'encounter' | 'immunization';
  clinicalTime?: string;
  recordedTime?: string;
  ingestedAt: string;
  title: string;
  summary?: string;
  source: { organization: string; resourceType: string; resourceId?: string };
  confidence: 'exact' | 'derived' | 'unknown';
};
```

## Build stable, source-aware keys

Prefer a stable business identifier within its namespace. Otherwise use the source connection, resource type, and logical ID. A content hash can help detect changes, but it should not be the sole real-world identity because clinically distinct events can contain identical data.

## Make refreshes incremental and reversible

1. **1. Read the next change window** Use a source cursor, supported history, or bounded updated-since query.
2. **2. Upsert source records** Preserve version, last-updated, and the raw-to-normalized mapping.
3. **3. Rebuild affected events** Regenerate projections deterministically for changed source records.
4. **4. Mark removals explicitly** Use tombstones or lifecycle state rather than silently deleting history.
5. **5. Commit the cursor** Advance progress only after the data and projection transaction succeeds.

## Design the timeline for uncertainty

- Group events with date-only precision separately from exact timestamps when ordering could mislead.
- Show source organization and freshness in the detail view.
- Do not infer clinical causality from adjacent events.
- Allow users to inspect the underlying coded concept and original text.
- Indicate when one source or category is unavailable or still syncing.

### Primary sources

- [FHIR R4 resource identity](https://hl7.org/fhir/R4/resource.html)
- [FHIR R4 Provenance](https://hl7.org/fhir/R4/provenance.html)

## Frequently asked questions

### What is a longitudinal patient record?

It is a record assembled over time and often across sources. A reliable implementation preserves source, clinical dates, update times, and completeness instead of presenting every event as equally certain.

### How should FHIR records be ordered?

Use the clinically relevant date for display, not Bundle order or ingestion time. Keep date precision and uncertainty visible.

### How do I prevent duplicate timeline events?

Use stable source-aware identity, business identifiers where reliable, version lineage, and conservative domain rules. Do not deduplicate only by display text.

### Can this timeline provide medical advice?

No. It is a data organization pattern. Clinical interpretation and decision support require separate validation, governance, and regulatory analysis.