Skip to main content
A schema is the single source of truth for the shape of your configuration. You declare it once using defineConfig(), and TypeScript uses it to infer exact types for every read and write operation — eliminating entire categories of bugs where stored data diverges from what your code expects.

defineConfig(schema)

defineConfig() accepts a plain schema object and returns it unchanged. Its only job is to act as a type anchor so TypeScript can infer the configuration shape throughout your app. Call it once at module level and export the result.
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],
});

Schema value types

Every field in your schema object must be one of the following value types:
TypeDescription
StringString field
NumberNumber field (must be finite)
BooleanBoolean field
keyring(Type, { id })OS keyring-protected field
optional(Type)Optional field (may be undefined or null)
{ ... }Nested object
[Type]Array of a single element type
Arrays must contain exactly one element — the element type — such as [String] or [{ name: String }]. This single-element tuple syntax is how the plugin knows what type to enforce for every item in the array.

Type inference

The plugin exports two utility types that derive TypeScript interfaces directly from your schema:
  • InferUnlocked<S> — The full type with keyring fields as their real value types. Use this when you have access to decrypted data.
  • InferLocked<S> — The type with keyring fields replaced by null. This is what you receive from .run() after a load or create, before calling .unlock().
import type { InferUnlocked, InferLocked } from "tauri-plugin-configurate-api";

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

type LockedConfig = InferLocked<typeof schema>;
// {
//   theme: string;
//   fontSize?: number;
//   apiKey: null;
//   database: { host: string; port: number; password: null };
//   tags: string[];
// }

keyring(typeCtor, { id })

keyring() marks a field so its value is stored in the OS keychain instead of the config file on disk. At runtime the config file contains a placeholder; the real value is written to and read from the platform keyring.
keyring(String, { id: "api-key" })
keyring(Number, { id: "pin-code" })
keyring(Boolean, { id: "consent-flag" })
The id must satisfy two rules:
  • It must be a non-empty string.
  • It must not contain /, because the plugin uses it as part of the OS keyring user string in the form {account}/{id}.
Each id must also be unique within the same schema. Reusing an id is a compile-time error enforced by TypeScript’s type system via HasDuplicateKeyringIds, and a runtime error thrown by defineConfig():
Duplicate keyring id: 'api-key'. Each keyring() call must use a unique id within the same schema.

optional(schema)

optional() wraps any schema value to indicate the field may be absent from stored data. Validation will not fail when the field is missing, and the inferred TypeScript type includes | undefined. optional() can wrap any of the following:
  • A primitive constructor: optional(Number)
  • A keyring field: optional(keyring(String, { id: "opt-secret" }))
  • A nested object: optional({ host: String, port: Number })
  • An array: optional([String])
const schema = defineConfig({
  theme: String,                              // required string
  fontSize: optional(Number),                 // optional number
  proxy: optional({ host: String, port: Number }), // optional nested object
  aliases: optional([String]),                // optional array of strings
  vaultKey: optional(keyring(String, { id: "vault" })), // optional keyring field
});