> ## Documentation Index
> Fetch the complete documentation index at: https://configurate.crystaworld.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Partially Update Your Config with Deep-Merge Patching

> Use config.patch() to update only specific keys in your config without overwriting the rest, with support for keyring fields and automatic creation.

When a user changes a single setting — say, the app theme — you should not need to read the entire config, modify it in memory, and write all fields back. `config.patch()` handles this for you: it deep-merges the partial object you provide into the stored config, leaving every key you omit completely untouched. This guide explains how patching works, how it interacts with keyring-protected fields, and how to handle configs that might not yet exist.

## How Patching Works

`config.patch(partial)` accepts a `Partial<InferUnlocked<S>>` and sends it to the plugin, which reads the current file, merges your partial data on top, and writes the result back. Only the keys present in your partial object are updated; everything else is preserved exactly as stored.

The merge follows **JSON Merge Patch semantics (RFC 7396)**:

* **Omitting a key** leaves the stored value unchanged.
* **Setting a key to `null`** writes `null` into the stored file — it does not delete or skip the key.
* **Providing a nested object** recursively merges at that level, so you can update a single nested property without supplying the rest of the object.

`config.patch()` returns a `LazyPatchEntry`. You complete the operation by chaining a terminal method.

| Terminal method      | Description                                               | Returns                      |
| -------------------- | --------------------------------------------------------- | ---------------------------- |
| `.run()`             | Patch without keyring                                     | `Promise<PatchedConfig<S>>`  |
| `.lock(opts).run()`  | Patch and store keyring secrets                           | `Promise<PatchedConfig<S>>`  |
| `.unlock(opts)`      | Patch, store keyring secrets, return unlocked result      | `Promise<UnlockedConfig<S>>` |
| `.createIfMissing()` | (modifier) Create config if not found instead of throwing | `LazyPatchEntry<S>`          |

<Warning>
  Setting a key to `null` in your patch object **writes `null` to disk** — it
  does not clear the field the way you might expect from other update APIs. If
  you want to reset a field to a default value, provide that default value
  explicitly, or use `config.save()` to replace the entire file.
</Warning>

***

## Basic Patch

Pass only the fields you want to change. All other stored keys remain as they are.

```typescript theme={null}
import {
  BaseDirectory,
  Configurate,
  JsonProvider,
  defineConfig,
  keyring,
} from "tauri-plugin-configurate-api";

const schema = defineConfig({
  theme: String,
  database: {
    host: String,
    password: keyring(String, { id: "db-password" }),
  },
});

const config = new Configurate({
  schema,
  fileName: "app.json",
  baseDir: BaseDirectory.AppConfig,
  provider: JsonProvider(),
});

const KEYRING = { service: "my-app", account: "default" };

// Only `theme` changes; `database` is untouched
const patched = await config.patch({ theme: "dark" }).run();
console.log(patched.data.theme); // "dark"
```

The returned `PatchedConfig<S>` exposes `data` typed as `Partial<InferLocked<S>>` — reflecting only the keys that were part of the patch, with keyring fields as `null` in the locked form.

***

## Patching Keyring Fields

If your partial data includes a keyring-protected field, you must chain `.lock(opts)` before `.run()` so the plugin knows where to store the updated secret.

```typescript theme={null}
// Update the database host and rotate the password
const patched = await config
  .patch({ database: { host: "db.prod.example.com", password: "new-secret" } })
  .lock(KEYRING)
  .run();
```

***

## Get Unlocked Data After Patching

Chain `.unlock(opts)` instead of `.lock(opts).run()` to receive an `UnlockedConfig<S>` with keyring fields already populated — useful when you need to display or act on the new values immediately after patching.

```typescript theme={null}
const unlocked = await config
  .patch({ theme: "light" })
  .unlock(KEYRING);

console.log(unlocked.data.theme);                   // "light"
console.log(unlocked.data.database.password);       // "secret" (from keyring)
```

***

## Create If Missing

By default, patching a config that does not yet exist throws an error. Chain `.createIfMissing()` before `.run()` to create the file with the provided partial data instead of throwing.

<Note>
  Patching a config that does not exist will throw unless you chain
  `.createIfMissing()`. If you are unsure whether the file has been created
  yet, either check with `config.exists()` first or use `.createIfMissing()`
  as a safe default.
</Note>

```typescript theme={null}
// Creates "app.json" with { theme: "dark" } if it doesn't exist yet;
// otherwise merges { theme: "dark" } into the existing file.
await config.patch({ theme: "dark" }).createIfMissing().run();
```

You can combine `.createIfMissing()` with `.lock()` when your partial data contains keyring fields:

```typescript theme={null}
await config
  .patch({ theme: "dark", database: { host: "localhost", password: "secret" } })
  .createIfMissing()
  .lock(KEYRING)
  .run();
```
