Versioning & Time Travel

Track changes to your data over time and query historical states

Overview

Stabilize ORM provides built-in versioning that tracks every change to your records. When enabled, a <table>_history table stores all historical versions with timestamps, operation types, and version numbers.

Enabling Versioning

Enable versioning on a model by setting versioned: true:

models/product.ts
import { defineModel, DataTypes } from "stabilize-orm";
export const Product = defineModel({
tableName: "products",
versioned: true, // Enable version tracking
columns: {
id: { type: DataTypes.STRING, required: true, unique: true },
name: { type: DataTypes.STRING, length: 255, required: true },
price: { type: DataTypes.DECIMAL, required: true },
},
});

How It Works

When versioning is enabled, Stabilize automatically creates a history table (e.g., products_history) that stores all historical versions. Each version includes:

  • All column values at that point in time
  • Version number (auto-incremented)
  • Operation type (insert, update, delete)
  • Valid from/to timestamps
  • Modified by and modified at audit fields

Version history is written automatically on create, update, and delete operations.

Time Travel Queries

Query the state of a record at a specific point in time using the asOf method. Pass the record ID and a date:

typescript
const repo = orm.getRepository(Product);
// Get product as it existed on a specific date
const pastDate = new Date("2025-01-01T00:00:00Z");
const productAsOf = await repo.asOf(product.id, pastDate);
console.log("Name on Jan 1:", productAsOf?.name);
console.log("Price on Jan 1:", productAsOf?.price);

Version History

Retrieve the complete change history for a record using history(id):

typescript
const repo = orm.getRepository(Product);
// Get all versions of a product
const versions = await repo.history(product.id);
console.log(`Product has ${versions.length} versions`);
versions.forEach(v => {
console.log(`Version ${v.version}:`);
console.log(` Name: ${v.name}`);
console.log(` Price: ${v.price}`);
console.log(` Operation: ${v.operation}`);
console.log(` Valid from: ${v.valid_from}`);
});

Rollback

Restore a record to a previous version using rollback(id, version). This creates a new version with the old data:

typescript
const repo = orm.getRepository(Product);
// Rollback product #1 to version 2
await repo.rollback(product.id, 2);
// The rollback creates a NEW version (version N+1) with version 2's data
const current = await repo.findOne(product.id);
console.log("Current version:", current.version); // Latest version number
console.log("Data restored from version 2");

Performance Considerations

  • Versioning adds storage overhead as all versions are retained
  • Write operations are slightly slower due to history table inserts
  • Consider archiving old versions for long-running applications
  • History queries are indexed on id and version for fast lookups