Schema & Config
Which Prisma schema features IndexedDB supports, and what prisma.config.ts contains
This page lists what a Prisma schema can contain when it targets IndexedDB, and what each field in prisma.config.ts does. For a first setup, follow the Quick Start instead.
Scalar types
| Prisma type | JavaScript value | Notes |
|---|---|---|
String | string | |
Int | number | |
Float | number | |
Boolean | boolean | Can't be a key or indexed. See Indexes. |
DateTime | Date | Compared and sorted by time, not by object identity. |
BigInt | bigint | Can't be a key or indexed. |
Decimal | string | |
Json | any JSON value | Can't be a key or indexed. |
Bytes | Uint8Array | Compared and sorted byte by byte. |
Add ? to make a field optional. Its value is then null when it isn't set.
Models and stores
Each model becomes an IndexedDB object store. The store is named after the model with its first letter lowercased, so model TodoItem is stored in todoItem. The same name is the model's key on the client: db.orm.todoItem.
Use @@map to choose a different name:
model Todo {
id String @id
title String
@@map("todos")
}const todos = await db.orm.todos.all();Primary keys
Every model needs a primary key.
@idon one field, or@@id([a, b])for a compound key.- Key fields must be
String,Int,Float,DateTime,DecimalorBytes, the types IndexedDB can use as keys. - In a compound key, order matters:
@@id([a, b])and@@id([b, a])are different keys.findUnique()anddelete()take the values in the order you declared them. @default(autoincrement())is supported on a single-fieldIntkey, not on a compound key.
Indexes
| Attribute | Creates |
|---|---|
@unique | A unique index on one field. |
@@unique([a, b]) | A unique compound index. |
@@index([a]) | A non-unique index. |
@@index([a, b]) | A non-unique compound index. |
Pass name: to choose an index's name, for example @@index([userId], name: "byUser"). Without one, the name is the field names joined with _, plus _unique for a unique index. A field can't appear twice in one index.
Like keys, indexed fields must be String, Int, Float, DateTime, Decimal or Bytes. IndexedDB silently leaves other values out of an index, so contract emit rejects an index on a Boolean, BigInt or Json field.
Queries use an index automatically for equality filters on an indexed field.
Defaults
| Attribute | Sets the field to |
|---|---|
@default("text"), @default(0), @default(true) | That literal value, on create. |
@default(now()) | The current time, on create. |
@default(uuid()) | A random UUID v4, on create. uuid(7) gives a UUID v7. |
@default(cuid()) | A CUID2, on create. |
@default(autoincrement()) | The next number, assigned by IndexedDB. Single Int keys only. |
@updatedAt | The current time, on create and on every update. |
A default only applies when the field is left out. It also counts as setting the field: a foreign key filled in by a default is checked like one you set yourself.
Relations
Declare relations the usual way, with @relation(fields: [...], references: [...]) on the side that holds the foreign key.
model Todo {
id String @id
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}- One-to-many, many-to-one and one-to-one relations are supported. Many-to-many relations aren't; use an explicit join model.
- Compound foreign keys are supported:
@relation(fields: [orgId, userId], references: [orgId, id]). onDeleteandonUpdateacceptCascade,SetNull,SetDefault,RestrictandNoAction. Both default toRestrict.NoActionbehaves likeRestrict.
IndexedDB has no foreign keys of its own, so the client enforces them. See Foreign keys and referential actions for exactly what it checks.
Server-only fields and models
When you sync with a server, mark fields and models that must never reach the browser with @idb.exclude (a field) or @@idb.exclude (a model). The browser's contract leaves them out when your config sets projection: "client":
contract: prismaIdbContract("src/prisma/schema.prisma", { projection: "client" }),Client Contracts explains the rules, including what happens to a relation that points at an excluded model.
Config
prisma.config.ts wraps an orm section built with defineConfig from @prisma-idb/family-idb/config-types. The Quick Start shows a complete file.
| Field | Value for IndexedDB | What it does |
|---|---|---|
family | @prisma-idb/family-idb/control | The database family. |
target | @prisma-idb/target-idb/control | The target within the family. Carries the migration planner. |
adapter | @prisma-idb/adapter-idb/control | Maps Prisma types to how IndexedDB stores them. |
driver | @prisma-idb/driver-idb/control | Required by the framework. No CLI command can reach a browser database, so this is a stub. |
db.connection | Any string, such as ":memory:" | Required by the framework, unused. The database name is chosen in the browser, with dbName. |
contract | prismaIdbContract("path/to/schema.prisma", { projection }) | The schema to read. contract emit writes contract.json and contract.d.ts next to it. |
migrations.dir | "migrations" | Where prisma-idb migration plan writes migration packages. |
Both prisma and prisma-idb read this file, so neither needs --contract or --migrations-dir flags. Pass --config <path> if the file isn't in the project root.