> ## 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.

# Schema Definition Functions: defineConfig, keyring, optional

> Reference for defineConfig(), keyring(), and optional() — the three functions for declaring type-safe configuration schemas in tauri-plugin-configurate.

A schema is a plain TypeScript object where each key maps to a schema value — a primitive constructor, a `keyring()` field, an `optional()` wrapper, a nested object, or a single-element array. You pass the schema to `defineConfig()` once at module level, and tauri-plugin-configurate derives all TypeScript types from it automatically. You never have to write the config interface by hand.

## defineConfig(schema)

Validates and freezes a schema object at runtime. The return value is the same object you passed in — `defineConfig()` exists so TypeScript can infer the generic parameter `S` and propagate it into every downstream type.

```typescript theme={null}
function defineConfig<S extends SchemaObject>(schema: S): S
```

<ParamField path="schema" type="S extends SchemaObject" required>
  A plain object where each value is a primitive constructor (`String`, `Number`, `Boolean`), a `keyring()` field, an `optional()` field, a nested schema object, or a single-element array like `[String]`. Arrays must contain exactly one element.
</ParamField>

<ResponseField name="returns" type="S">
  The same schema object passed in, typed as `S`. Assign the return value to a `const` so that TypeScript treats it as a literal type rather than a widened one.
</ResponseField>

At runtime, `defineConfig()` performs two checks and throws on failure:

* Every schema array (`[String]`, `[Number]`, etc.) must contain exactly one element.
* Every `keyring()` id within the schema must be unique.

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

const schema = defineConfig({
  theme: String,
  fontSize: optional(Number),
  apiKey: keyring(String, { id: "api-key" }),
  database: {
    host: String,
    port: Number,
    password: keyring(String, { id: "db-password" }),
  },
  tags: [String],
});
```

<Note>
  TypeScript enforces unique keyring IDs at the type level via `HasDuplicateKeyringIds<S>`. If you accidentally reuse an ID, the TypeScript compiler rejects the call before it reaches the runtime check.
</Note>

***

## keyring(typeCtor, opts)

Marks a schema field as OS keyring-protected. The value is stored in the operating system's credential store (not in the config file) and is returned as `null` when loading without an unlock step.

```typescript theme={null}
function keyring<T, Id extends string>(
  typeCtor: StringConstructor | NumberConstructor | BooleanConstructor,
  opts: { id: Id }
): KeyringField<T, Id>
```

<ParamField path="typeCtor" type="StringConstructor | NumberConstructor | BooleanConstructor" required>
  The JavaScript constructor function for the value's type: `String`, `Number`, or `Boolean`.
</ParamField>

<ParamField path="opts.id" type="string" required>
  Unique identifier for this keyring entry within the schema. Must not be empty. Must not contain `/` — the slash is used as a separator in the keyring user string (`{account}/{id}`), so including it would create an ambiguous path.
</ParamField>

<ResponseField name="returns" type="KeyringField<T, Id>">
  A branded marker object. At runtime this object is a plain record with internal brand keys; its shape matters only to the plugin internals and the TypeScript type system.
</ResponseField>

```typescript theme={null}
const schema = defineConfig({
  apiKey: keyring(String, { id: "api-key" }),
  retryCount: keyring(Number, { id: "retry-count" }),
  isPremium: keyring(Boolean, { id: "premium-flag" }),
});
```

<Warning>
  Two `keyring()` calls in the same schema must not share the same `id`. Reusing an id will cause `defineConfig()` to throw at runtime and produce a TypeScript compile error.
</Warning>

***

## optional(schema)

Wraps any schema value to mark it as optional. An optional field may be absent from the stored config; it does not fail schema validation when missing, and its inferred TypeScript type includes `| undefined`.

```typescript theme={null}
function optional<V>(schema: V): OptionalField<V>
```

<ParamField path="schema" type="V" required>
  Any valid schema value: a primitive constructor, a `keyring()` field, a nested schema object, or a single-element array. You cannot nest `optional()` inside another `optional()`.
</ParamField>

<ResponseField name="returns" type="OptionalField<V>">
  A branded marker object that wraps `schema` and signals optionality to the type inference utilities and runtime validation.
</ResponseField>

```typescript theme={null}
const schema = defineConfig({
  theme: String,                                       // required string
  fontSize: optional(Number),                         // optional number
  apiKey: optional(keyring(String, { id: "key" })),   // optional keyring field
  proxy: optional({ host: String, port: Number }),    // optional nested object
  aliases: optional([String]),                        // optional array of strings
});
```

***

## Schema types reference

The table below shows every schema value type and the TypeScript type it produces through `InferUnlocked<S>` and `InferLocked<S>`.

| Schema value                     | `InferUnlocked<S>`    | `InferLocked<S>`      |
| -------------------------------- | --------------------- | --------------------- |
| `String`                         | `string`              | `string`              |
| `Number`                         | `number`              | `number`              |
| `Boolean`                        | `boolean`             | `boolean`             |
| `keyring(String, ...)`           | `string`              | `null`                |
| `keyring(Number, ...)`           | `number`              | `null`                |
| `keyring(Boolean, ...)`          | `boolean`             | `null`                |
| `optional(String)`               | `string \| undefined` | `string \| undefined` |
| `optional(keyring(String, ...))` | `string \| undefined` | `null \| undefined`   |
| `{ key: String }`                | `{ key: string }`     | `{ key: string }`     |
| `[String]`                       | `string[]`            | `string[]`            |
| `[keyring(String, ...)]`         | `string[]`            | `null[]`              |

***

## Type inference utilities

Two utility types let you reference the inferred config shape in your own code:

### InferUnlocked\<S>

Produces the full config type with all keyring fields resolved to their real value types. Use this when you have an unlocked config or when constructing data to pass to `create()`, `save()`, or `reset()`.

```typescript theme={null}
import type { InferUnlocked } from "tauri-plugin-configurate-api";

type AppConfig = InferUnlocked<typeof schema>;
// { theme: string; fontSize?: number; apiKey: string; database: { host: string; port: number; password: string } }

function buildDefaults(): Partial<AppConfig> {
  return { theme: "light", fontSize: 14 };
}
```

### InferLocked\<S>

Produces the config type with all keyring fields replaced by `null`. This is the type of `LockedConfig<S>.data` — what you see when you call `load().run()` without unlocking.

```typescript theme={null}
import type { InferLocked } from "tauri-plugin-configurate-api";

type LockedAppConfig = InferLocked<typeof schema>;
// { theme: string; fontSize?: number; apiKey: null; database: { host: string; port: number; password: null } }
```
