Query complete change history for files and entities using SQL.
History walks the commit graph backwards from a starting point:
Querying from Checkpoint 3 traverses backwards, showing states at C3, C2, and C1. The lixcol_depth field indicates distance from the starting point (0 = starting point, 1 = one step back, etc.).
The commit graph is global and shared across all versions. You query at a specific commit, not a version. All versions reference the same unified graph structure.
Query file state at specific checkpoints to see how it changed over time:
import { openLix, createCheckpoint } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";// Query file state at each checkpoint to show history
const history = [];
for (const checkpoint of [checkpoint3, checkpoint2, checkpoint1]) {
const state = await lix.db
.selectFrom("file_history")
.where("path", "=", "/config.json")
.where("lixcol_root_commit_id", "=", checkpoint.id)
.where("lixcol_depth", "=", 0)
.select(["data"])
.executeTakeFirstOrThrow();
history.push({
checkpoint: checkpoint.id,
data: JSON.parse(new TextDecoder().decode(state.data))
});
}
console.log("File history:", history);Query file_history with lixcol_root_commit_id = checkpoint.id and lixcol_depth = 0 to get the file state at that specific checkpoint.
Query history for specific entities like key-value pairs, custom data types, or plugin-defined entities:
import { openLix, createCheckpoint } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
// Query file state at each checkpoint to show history
const history = [];
for (const checkpoint of [checkpoint3, checkpoint2, checkpoint1]) {
const state = await lix.db
.selectFrom("file_history")
.where("path", "=", "/config.json")
.where("lixcol_root_commit_id", "=", checkpoint.id)
.where("lixcol_depth", "=", 0)
.select(["data"])
.executeTakeFirstOrThrow();
history.push({
checkpoint: checkpoint.id,
data: JSON.parse(new TextDecoder().decode(state.data))
});
}
console.log("File history:", history);// Create a key-value pair to demonstrate entity-level history
await lix.db
.insertInto("key_value")
.values({ key: "app_version", value: "1.0.0" })
.execute();
await createCheckpoint({ lix });
await lix.db
.updateTable("key_value")
.where("key", "=", "app_version")
.set({ value: "1.1.0" })
.execute();
const checkpoint4 = await createCheckpoint({ lix });
// Query history for the key-value entity from checkpoint 4
const entityHistory = await lix.db
.selectFrom("state_history")
.where("entity_id", "=", "app_version")
.where("schema_key", "=", "lix_key_value")
.where("root_commit_id", "=", checkpoint4.id)
.orderBy("depth", "asc")
.select(["snapshot_content", "depth"])
.execute();
console.log("Entity history for app_version:",
entityHistory.map(h => ({
value: h.snapshot_content.value,
depth: h.depth
}))
);Entity-level history enables fine-grained audit trails and tracking for individual data points beyond files.
Query file state at any specific checkpoint:
import { openLix, createCheckpoint } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
// Query file state at each checkpoint to show history
const history = [];
for (const checkpoint of [checkpoint3, checkpoint2, checkpoint1]) {
const state = await lix.db
.selectFrom("file_history")
.where("path", "=", "/config.json")
.where("lixcol_root_commit_id", "=", checkpoint.id)
.where("lixcol_depth", "=", 0)
.select(["data"])
.executeTakeFirstOrThrow();
history.push({
checkpoint: checkpoint.id,
data: JSON.parse(new TextDecoder().decode(state.data))
});
}
console.log("File history:", history);
// Create a key-value pair to demonstrate entity-level history
await lix.db
.insertInto("key_value")
.values({ key: "app_version", value: "1.0.0" })
.execute();
await createCheckpoint({ lix });
await lix.db
.updateTable("key_value")
.where("key", "=", "app_version")
.set({ value: "1.1.0" })
.execute();
const checkpoint4 = await createCheckpoint({ lix });
// Query history for the key-value entity from checkpoint 4
const entityHistory = await lix.db
.selectFrom("state_history")
.where("entity_id", "=", "app_version")
.where("schema_key", "=", "lix_key_value")
.where("root_commit_id", "=", checkpoint4.id)
.orderBy("depth", "asc")
.select(["snapshot_content", "depth"])
.execute();
console.log("Entity history for app_version:",
entityHistory.map(h => ({
value: h.snapshot_content.value,
depth: h.depth
}))
);// Query file state at a specific checkpoint
const stateAtCheckpoint2 = await lix.db
.selectFrom("file_history")
.where("path", "=", "/config.json")
.where("lixcol_root_commit_id", "=", checkpoint2.id)
.where("lixcol_depth", "=", 0)
.select(["data"])
.executeTakeFirstOrThrow();
console.log("State at checkpoint 2:",
JSON.parse(new TextDecoder().decode(stateAtCheckpoint2.data))
);Use lixcol_depth = 0 to get the state at that exact checkpoint.
Lix automatically commits every change. Checkpoints are optional labels for important moments. Always filter by lixcol_root_commit_id to scope history to a specific point and avoid mixing timelines.