Skip to main content
The TypeScript SDK ships two standalone utilities alongside the Configurate class. configDiff computes a structural diff between two config objects — useful for auditing changes before or after a save. MigrationStep is the interface you implement when using the version and migrations constructor options to evolve config schemas over time.

configDiff(oldData, newData)

Computes a structural diff between two config objects. Returns an array of DiffEntry objects that describe every key that was added, removed, or changed between oldData and newData. Nested objects are compared recursively, and each difference is reported using a dot-separated path (e.g. "database.host").
import { configDiff } from "tauri-plugin-configurate-api";

Signature

function configDiff(
  oldData: Record<string, unknown>,
  newData: Record<string, unknown>,
): DiffEntry[]

Parameters

oldData
Record<string, unknown>
required
The previous config object — the baseline to compare from.
newData
Record<string, unknown>
required
The new config object — the state to compare to.

Returns DiffEntry[]

An array of change descriptors. Each entry covers exactly one leaf-level difference. If no keys differ, the array is empty.
path
string
required
Dot-separated path to the changed key (e.g. "theme" for a top-level key, "database.host" for a nested one).
type
"added" | "removed" | "changed"
required
The kind of change. "added" means the key is present in newData but not in oldData. "removed" means the opposite. "changed" means the key exists in both but the value differs.
oldValue
unknown
The previous value. Present for "removed" and "changed" entries; absent for "added".
newValue
unknown
The new value. Present for "added" and "changed" entries; absent for "removed".

Recursive dot-path comparison

When both oldData[key] and newData[key] are plain objects, configDiff recurses into them rather than treating the whole object as a single changed value. The recursive call prepends the current key to form the dot-separated path reported in each DiffEntry. Non-object values (strings, numbers, booleans, arrays, null) are compared with deep equality. Arrays with different lengths or different elements at any index are reported as a single "changed" entry at their path — array elements are not diffed individually.

Example

import { configDiff } from "tauri-plugin-configurate-api";

const old = {
  theme: "light",
  fontSize: 14,
  database: { host: "localhost", port: 5432 },
};

const next = {
  theme: "dark",
  fontSize: 14,
  database: { host: "db.example.com", port: 5432 },
  lang: "en",
};

const changes = configDiff(old, next);
// [
//   { path: "theme", type: "changed", oldValue: "light", newValue: "dark" },
//   { path: "database.host", type: "changed", oldValue: "localhost", newValue: "db.example.com" },
//   { path: "lang", type: "added", newValue: "en" },
// ]
configDiff is a pure utility and performs no IPC calls. It does not read from or write to disk — pass it the .data properties from two LockedConfig or UnlockedConfig instances to compare them in memory.

DiffEntry

The shape of each element in the array returned by configDiff.
interface DiffEntry {
  path: string;
  type: "added" | "removed" | "changed";
  oldValue?: unknown;
  newValue?: unknown;
}

MigrationStep

Used with the version and migrations constructor options to define schema versioning. Each step describes how to transform config data from one schema version to the next. Migrations run automatically on every load() call when the stored version is lower than the current version.
interface MigrationStep<TData extends Record<string, unknown>> {
  version: number;
  up: (data: TData) => TData;
}
version
number
required
The schema version this migration upgrades from. A step with version: 1 transforms data stored at schema version 1 into version 2 data. Steps must be listed in ascending order.
up
(data: TData) => TData
required
A pure transform function. Receives the config data at version and must return data compatible with version + 1. Return a new object — avoid mutating data in place.

Example

import {
  Configurate,
  JsonProvider,
  defineConfig,
  BaseDirectory,
} from "tauri-plugin-configurate-api";

const schema = defineConfig({
  theme: String,
  language: String,
  newKey: String,
});

const config = new Configurate({
  schema,
  fileName: "app.json",
  baseDir: BaseDirectory.AppConfig,
  provider: JsonProvider(),
  version: 2,
  migrations: [
    {
      // Upgrades data stored at v0 → v1: adds the `language` field
      version: 0,
      up: (data) => ({ ...data, language: "en" }),
    },
    {
      // Upgrades data stored at v1 → v2: renames `oldKey` to `newKey`
      version: 1,
      up: (data) => {
        const { oldKey, ...rest } = data as typeof data & { oldKey?: unknown };
        return { ...rest, newKey: oldKey ?? "default" };
      },
    },
  ],
});
When migrations run, the migrated data is automatically saved back to storage so the next load() does not re-run the same steps. A failed auto-save is logged as a warning but is non-fatal — the migrated data is still returned for the current session.

Build docs developers (and LLMs) love