Schema & Config
Write PSL and point Prisma Next at the IDB target
Prisma Next IDB reads PSL directly. No generator block and no Prisma Client needed.
Schema
model User {
id String @id
name String
email String? @unique
todos Todo[]
}
model Todo {
id String @id
title String
done Boolean
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId], name: "byUser")
}Supported scalar types: String, Int, Float, Boolean, DateTime, BigInt, Decimal, Json, Bytes.
IDB rules
- Each model needs one scalar
@idfield or a single-field@@id([field]). - Compound primary keys are not supported.
- Compound indexes are not supported.
- Relation fields must declare
@relation(fields: [...], references: [...])on the foreign-key side. - Use
@@map("storeName")to control the IndexedDB object store name. The store name is also the key underdb.orm.
model Todo {
id String @id
title String
@@map("todos")
}const todos = await db.orm.todos.all();Client-only fields and models
If you sync a schema to a server (see Sync), some fields or models should never reach the browser: password hashes, session tokens, internal audit tables. Mark them with the IDB-family PSL attributes @idb.exclude (field) and @@idb.exclude (model):
model User {
id String @id
name String
password String @idb.exclude // dropped from the client contract
boards Board[]
sessions Session[]
}
model Session {
id String @id
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@idb.exclude // whole model dropped from the client contract
}Your IDB config (prismaIdbContract(path, { projection: "client" })) strips these before the browser ever sees them. That's the only projection flag you touch on the client side — "full" is just the interpreter's default (nothing stripped), not a second thing you feed to a server config.
If your server is also on the IDB family, you'd point it at the same schema without the projection option. But the realistic case — and what the sync server setup demonstrates — is a SQL/Postgres server, which can't parse @idb.exclude at all (its parser hard-errors on the unrecognized namespace). For that you don't touch projection here — see Client Contracts for the writeSqlSchemaWithSync helper that handles it, and what happens to a relation that points at an excluded model.
A few rules the interpreter enforces:
- The
@idfield cannot be@idb.excluded. - An index cannot reference an excluded field.
- An excluded field cannot also be
@unique.
Config
import { defineConfig } from "@prisma-next-idb/family-idb/config-types";
import { prismaIdbContract } from "@prisma-next-idb/family-idb/contract-psl";
import idbFamily from "@prisma-next-idb/family-idb/control";
import idbTarget from "@prisma-next-idb/target-idb/control";
import idbAdapter from "@prisma-next-idb/adapter-idb/control";
import idbDriver from "@prisma-next-idb/driver-idb/control";
export default defineConfig({
family: idbFamily,
target: idbTarget,
adapter: idbAdapter,
driver: idbDriver,
db: { connection: ":memory:" },
contract: prismaIdbContract("src/prisma/schema.prisma"),
migrations: { dir: "migrations" },
});connection is present because the Prisma Next framework requires it. The IDB target does not use it. The prismaIdbContract path is the source schema; contract emit reads it and writes contract.json and contract.d.ts alongside it.