Schema definition
Define which columns to encrypt, what queries to support, and how to handle nested objects
Schema definition
Schemas tell the SDK which database columns to encrypt and what types of queries to support on the encrypted data.
Creating schema files
Declare your encryption schema in TypeScript — either in a single file or split across multiple files:
src/protect/
└── schema.ts # single filesrc/protect/schemas/
├── users.ts # per-table files
└── posts.tsDefining a schema
A schema maps your database tables and columns using encryptedTable and encryptedColumn:
import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
// TypeScript name Database table name
// ↓ ↓
export const protectedUsers = encryptedTable("users", {
// TypeScript name Database column name
// ↓ ↓
email: encryptedColumn("email"),
})Index types
Index types determine what queries you can run on encrypted data. Methods are chainable — call as many as you need on a single column.
export const protectedUsers = encryptedTable("users", {
email: encryptedColumn("email")
.equality() // exact match queries
.freeTextSearch() // full-text search
.orderAndRange(), // sorting and range queries
})| Method | Purpose | SQL equivalent |
|---|---|---|
.equality() | Exact match lookups | WHERE email = 'user@example.com' |
.freeTextSearch() | Full-text / fuzzy search | WHERE description LIKE '%example%' |
.orderAndRange() | Sorting, comparison, range queries | ORDER BY price ASC |
.searchableJson() | Encrypted JSONB path and containment queries | WHERE metadata @> '{"role":"admin"}' |
Only enable the indexes you need — each additional index type has a performance cost.
For columns storing JSON data,
.searchableJson()is the recommended index. It automatically configures the column for encrypted JSONB path and containment queries. See Searchable encryption for details.
Data types
Use .dataType() to specify the plaintext type for a column:
encryptedColumn("age").dataType("number").orderAndRange()Supported data types: 'string', 'number', 'boolean', 'date', 'bigint', 'json'.
Free-text search options
Customize the tokenizer and filter settings for .freeTextSearch():
encryptedColumn("bio").freeTextSearch({
tokenizer: { kind: "ngram", token_length: 3 }, // or { kind: "standard" }
token_filters: [{ kind: "downcase" }],
k: 6,
m: 2048,
include_original: false,
})Nested objects
CipherStash Encryption supports nested objects in your schema, allowing you to encrypt nested properties. You can define nested objects up to 3 levels deep using encryptedField.
Searchable encryption is not supported on nested objects. This is most useful for NoSQL databases or less structured data.
import { encryptedTable, encryptedColumn, encryptedField } from "@cipherstash/stack/schema"
export const protectedUsers = encryptedTable("users", {
email: encryptedColumn("email").equality().freeTextSearch(),
profile: {
name: encryptedField("profile.name"),
address: {
street: encryptedField("profile.address.street"),
location: {
coordinates: encryptedField("profile.address.location.coordinates"),
},
},
},
})When working with nested objects:
- Each level can have its own encrypted fields
- The maximum nesting depth is 3 levels
- Null and undefined values are supported at any level
- Optional nested objects are supported
Encrypted JSONB
For columns that store JSON objects, use .searchableJson() to enable encrypted JSONB queries:
const documents = encryptedTable("documents", {
metadata: encryptedColumn("metadata").searchableJson(),
})This enables both JSONPath selector queries and containment queries on the encrypted data.
Multiple tables
Pass multiple schemas when initializing the client:
import { Encryption } from "@cipherstash/stack"
const client = await Encryption({ schemas: [protectedUsers, documents] })Type inference
Infer plaintext and encrypted types from your schema:
import type { InferPlaintext, InferEncrypted } from "@cipherstash/stack/schema"
type UserPlaintext = InferPlaintext<typeof protectedUsers>
// { email: string; ... }
type UserEncrypted = InferEncrypted<typeof protectedUsers>
// { email: Encrypted; ... }Client-safe exports
For client-side code where the native FFI module is not available, import schema builders from the @cipherstash/stack/client subpath:
import { encryptedTable, encryptedColumn } from "@cipherstash/stack/client"This exports schema builders and types only — no native module dependency.