CipherStashDocs
IntegrationsPrisma ORM

Schema and queries

CipherStash column constructors, runtime envelopes, and encrypted query operators for Prisma ORM 8.

The CipherStash Prisma extension separates the encrypted column type from the runtime value envelope:

  • In schema.prisma, a domain-named constructor such as TextEq() fixes the Postgres plaintext family and searchable capabilities.
  • In application code, a primitive-named envelope such as EncryptedString wraps a plaintext value for encryption.

Column constructors

Choose the plaintext type first, then the narrowest query capability your application needs.

PlaintextStorage onlyEqualityOrder and rangeFree-text onlyAll text queries
stringText()TextEq()TextOrd()TextMatch()TextSearch()
number (int4)Integer()IntegerEq()IntegerOrd()
number (int2)Smallint()SmallintEq()SmallintOrd()
bigint (int8)BigInt()BigIntEq()BigIntOrd()
number (numeric)Numeric()NumericEq()NumericOrd()
number (float4)Real()RealEq()RealOrd()
number (float8)Double()DoubleEq()DoubleOrd()
Date (date)Date()DateEq()DateOrd()
Date (timestamp)Timestamp()TimestampEq()TimestampOrd()
booleanBoolean()
JSON documentJson() supports containment and JSONPath queries

Every *Ord() constructor includes equality. TextMatch() provides token matching without equality; TextSearch() combines equality, range, ordering, and token matching. Bare constructors such as Text() are storage-only.

Each constructor maps directly to an EQL v3 Postgres domain. For example, cipherstash.DoubleOrd() creates an eql_v3_double_ord column. The type cannot be broadened at query time.

Runtime envelopes

Schema familyWrite envelope
Text*EncryptedString.from(value)
Integer*, Smallint*, Numeric*, Real*, Double*EncryptedNumber.from(value)
BigInt*EncryptedBigInt.from(value)
Date*, Timestamp*EncryptedDate.from(value)
BooleanEncryptedBoolean.from(value)
JsonEncryptedJson.from(value)

Import envelopes and decryptAll from @cipherstash/stack-prisma/runtime.

import {
  decryptAll,
  EncryptedDate,
  EncryptedNumber,
  EncryptedString,
} from "@cipherstash/stack-prisma/runtime"

await db.orm.public.User.create({
  id: "user-0",
  email: EncryptedString.from("[email protected]"),
  salary: EncryptedNumber.from(100_000),
  birthday: EncryptedDate.from(new Date("1990-01-01")),
})

const rows = await db.orm.public.User.all()
await decryptAll(rows)

decryptAll groups a result set by table and column to avoid one encryption-service round trip per value. Call .decrypt() on an envelope after the batch completes.

Query operators

Operators are generated only where the declared domain supports them.

OperatorMeaning
eqlEq(value), eqlNeq(value)Equality and inequality
eqlIn(values), eqlNotIn(values)Set membership
eqlMatch(term)Free-text token match
eqlGt/Gte/Lt/Lte(value)Range comparison
eqlBetween(low, high), eqlNotBetween(low, high)Bounded range
eqlAsc(column), eqlDesc(column)Encrypted ordering
eqlJsonContains(value)JSON containment
eqlJsonPathEq/Neq(path, value)Equality at a JSONPath
eqlJsonPathGt/Gte/Lt/Lte(path, value)Range comparison at a JSONPath
eqlJsonPathAsc/Desc(column, path)Ordering by a JSONPath leaf
import {
  eqlAsc,
  eqlJsonPathDesc,
} from "@cipherstash/stack-prisma/runtime"

const matches = await db.orm.public.User
  .where((user) => user.email.eqlMatch("example.com"))
  .all()

const earners = await db.orm.public.User
  .where((user) => user.salary.eqlBetween(100_000, 150_000))
  .orderBy((user) => eqlAsc(user.salary))
  .all()

const preferences = await db.orm.public.User
  .where((user) => user.profile.eqlJsonContains({ theme: "dark" }))
  .orderBy((user) => eqlJsonPathDesc(user.profile, "$.score"))
  .all()

Free-text search is token matching, not SQL ILIKE. Pass a term such as example.com, not a wildcard pattern such as %example.com%.

Indexes

Encrypted predicates require functional Postgres indexes at meaningful scale. schema.prisma cannot express expression indexes, so put the index DDL in a Prisma raw-SQL migration operation and apply it with prisma-next migrate.

The domain-to-index matrix, SQL recipes, rollout timing, ANALYZE, and EXPLAIN verification are maintained in EQL indexes.

Subpath exports

ImportPurpose
@cipherstash/stack-prisma/controlPrisma extension pack
@cipherstash/stack-prisma/v3cipherstashFromStack and the EQL v3 adapter
@cipherstash/stack-prisma/runtimeEnvelopes, decryptAll, and query helpers
@cipherstash/stack-prisma/column-typesTypeScript-authored contract factories

On this page