Versioning & Time Travel
Track changes to your data over time and query historical states
Overview
Stabilize ORM provides built-in versioning capabilities that allow you to track every change made to your records. This enables powerful features like time-travel queries, audit trails, and rollback functionality.
Enabling Versioning
Enable versioning on a model by setting the versioning option:
models/product.tstypescript
import { defineModel, DataTypes } from "stabilize-orm";export const Product = defineModel({tableName: "products",versioning: true, // Enable versioningcolumns: {id: {type: DataTypes.Integer,primaryKey: true,autoIncrement: true,},name: {type: DataTypes.String,length: 255,},price: {type: DataTypes.Decimal,precision: 10,scale: 2,},},});
How It Works
When versioning is enabled, Stabilize automatically creates a shadow table (e.g., products_history) that stores all historical versions of your records. Each version includes:
- All column values at that point in time
- Version number (auto-incremented)
- Timestamp of when the version was created
- Operation type (INSERT, UPDATE, DELETE)
Time Travel Queries
Query the state of your data at any point in time using the asOf method:
const repo = orm.getRepository(Product);// Get product state as of a specific dateconst product = await repo.asOf(new Date("2025-10-01")).findById(1);// Get all products as they were yesterdayconst yesterday = new Date();yesterday.setDate(yesterday.getDate() - 1);const products = await repo.asOf(yesterday).findAll();// Query with conditions at a specific timeconst expensiveProducts = await repo.asOf(new Date("2025-10-01")).query().where("price", ">", 100).findAll();
Version History
Retrieve the complete history of changes for a record:
const repo = orm.getRepository(Product);// Get all versions of a productconst history = await repo.history(1);// Returns array of versions:// [// { id: 1, name: "Widget", price: 19.99, version: 1, timestamp: "2025-01-01T10:00:00Z" },// { id: 1, name: "Widget Pro", price: 24.99, version: 2, timestamp: "2025-02-15T14:30:00Z" },// { id: 1, name: "Widget Pro", price: 29.99, version: 3, timestamp: "2025-03-20T09:15:00Z" },// ]// Get history with filtersconst recentHistory = await repo.history(1, {since: new Date("2025-02-01"),until: new Date("2025-03-01"),});
Rollback
Restore a record to a previous version:
const repo = orm.getRepository(Product);// Rollback to a specific versionawait repo.rollback(1, 2); // Rollback product #1 to version 2// Rollback to a specific dateawait repo.rollback(1, new Date("2025-10-01"));// Rollback creates a new version with the old dataconst product = await repo.findById(1);console.log(product.version); // Will be the latest version number
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
- Use indexes on the history table's timestamp column for faster queries
