Skip to content

@unthrown/prisma


@unthrown/prisma

Classes

ForeignKeyViolation

Defined in: packages/prisma/src/index.ts:64

A foreign key constraint was violated (Prisma error P2003).

Extends

  • TaggedErrorInstance<"ForeignKeyViolation", { cause: unknown; }>

Constructors

Constructor
ts
new ForeignKeyViolation(args): ForeignKeyViolation;

Defined in: packages/core/dist/index.d.mts:2034

Parameters
ParameterType
argsobject & object
Returns

ForeignKeyViolation

Inherited from
ts
TaggedError("ForeignKeyViolation")<{ cause: unknown }>.constructor

Properties

PropertyModifierTypeInherited fromDefined in
_tagreadonly"ForeignKeyViolation"TaggedError("ForeignKeyViolation")._tagpackages/core/dist/index.d.mts:2011
causepublicunknownTaggedError("ForeignKeyViolation").causenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts:24
messagepublicstringTaggedError("ForeignKeyViolation").messagenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1075
namepublicstringTaggedError("ForeignKeyViolation").namenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1074
stack?publicstringTaggedError("ForeignKeyViolation").stacknode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1076

InvalidCursor

Defined in: packages/prisma/src/index.ts:91

The cursor handed to withCursor could not be used — the caller's parseCursor rejected it, or the query it produced was one Prisma refuses.

Remarks

The one anticipated failure of pagination, and the reason it is modeled at all: a cursor is an opaque string from the outside world (a query parameter), so a client sending garbage is input you answer with a 400 — not a bug in your code. Every other pagination failure is a defect, like any other query.

Extends

  • TaggedErrorInstance<"InvalidCursor", { cause: unknown; }>

Constructors

Constructor
ts
new InvalidCursor(args): InvalidCursor;

Defined in: packages/core/dist/index.d.mts:2034

Parameters
ParameterType
argsobject & object
Returns

InvalidCursor

Inherited from
ts
TaggedError("InvalidCursor")<{ cause: unknown }>.constructor

Properties

PropertyModifierTypeInherited fromDefined in
_tagreadonly"InvalidCursor"TaggedError("InvalidCursor")._tagpackages/core/dist/index.d.mts:2011
causepublicunknownTaggedError("InvalidCursor").causenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts:24
messagepublicstringTaggedError("InvalidCursor").messagenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1075
namepublicstringTaggedError("InvalidCursor").namenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1074
stack?publicstringTaggedError("InvalidCursor").stacknode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1076

RecordNotFound

Defined in: packages/prisma/src/index.ts:78

A record required by the operation does not exist (Prisma errors P2025 and P2018) — the missing row of a findUniqueOrThrow, update, or delete, or the missing target of a nested connect.

Remarks

The nested-connect case is why tryCreate and tryUpsert carry this error despite never "missing" a row of their own: create({ data: { author: { connect: { id } } } }) raises P2025 when that author does not exist (and P2018 for the to-many side of the same mistake). Both codes say the same thing — a record the write depended on was not found — so both map here.

Extends

  • TaggedErrorInstance<"RecordNotFound", { cause: unknown; }>

Constructors

Constructor
ts
new RecordNotFound(args): RecordNotFound;

Defined in: packages/core/dist/index.d.mts:2034

Parameters
ParameterType
argsobject & object
Returns

RecordNotFound

Inherited from
ts
TaggedError("RecordNotFound")<{ cause: unknown }>.constructor

Properties

PropertyModifierTypeInherited fromDefined in
_tagreadonly"RecordNotFound"TaggedError("RecordNotFound")._tagpackages/core/dist/index.d.mts:2011
causepublicunknownTaggedError("RecordNotFound").causenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts:24
messagepublicstringTaggedError("RecordNotFound").messagenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1075
namepublicstringTaggedError("RecordNotFound").namenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1074
stack?publicstringTaggedError("RecordNotFound").stacknode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1076

UniqueConstraintViolation

Defined in: packages/prisma/src/index.ts:58

A unique constraint was violated (Prisma error P2002).

Remarks

fields carries the offending column set from the error's meta.target (empty when the driver does not report it).

Extends

  • TaggedErrorInstance<"UniqueConstraintViolation", { cause: unknown; fields: readonly string[]; }>

Constructors

Constructor
ts
new UniqueConstraintViolation(args): UniqueConstraintViolation;

Defined in: packages/core/dist/index.d.mts:2034

Parameters
ParameterType
argsobject & object
Returns

UniqueConstraintViolation

Inherited from
ts
TaggedError("UniqueConstraintViolation")<{
  fields: readonly string[];
  cause: unknown;
}>.constructor

Properties

PropertyModifierTypeInherited fromDefined in
_tagreadonly"UniqueConstraintViolation"TaggedError("UniqueConstraintViolation")._tagpackages/core/dist/index.d.mts:2011
causepublicunknownTaggedError("UniqueConstraintViolation").causenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts:24
fieldsreadonlyreadonly string[]TaggedError("UniqueConstraintViolation").fieldspackages/prisma/src/index.ts:59
messagepublicstringTaggedError("UniqueConstraintViolation").messagenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1075
namepublicstringTaggedError("UniqueConstraintViolation").namenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1074
stack?publicstringTaggedError("UniqueConstraintViolation").stacknode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1076

Type Aliases

CursorPaginationMeta

ts
type CursorPaginationMeta = object & 
  | {
  endCursor: string;
  startCursor: string;
}
  | {
  endCursor: null;
  startCursor: null;
};

Defined in: packages/prisma/src/pagination.ts:20

The page metadata of withCursor.

Type Declaration

NameTypeDefined in
hasNextPagebooleanpackages/prisma/src/pagination.ts:22
hasPreviousPagebooleanpackages/prisma/src/pagination.ts:21

Remarks

startCursor / endCursor are the cursors of the page's boundary rows, so they are null together exactly when the page is empty — checking one narrows the other. They are deliberately NOT coupled to the flags: the last page has hasNextPage: false with a non-null endCursor, and an empty page past the end has hasPreviousPage: true with a null startCursor.


CursorPaginationOptions

ts
type CursorPaginationOptions<Row, Cursor> = object & 
  | {
  after?: string;
  before?: never;
  limit: number;
}
  | {
  after?: never;
  before?: string;
  limit: number;
}
  | {
  after?: string;
  before?: never;
  limit: null;
};

Defined in: packages/prisma/src/pagination.ts:47

Options of withCursor, in the style of prisma-extension-pagination.

Type Declaration

NameTypeDescriptionDefined in
getCursor()?(row) => stringSerialize a row into an opaque cursor. Defaults to String(row.id).packages/prisma/src/pagination.ts:49
parseCursor()?(cursor) => CursorParse an opaque cursor back into the model's cursor input.packages/prisma/src/pagination.ts:51

Type Parameters

Type ParameterDescription
Rowthe (selection-narrowed) result row type.
Cursorthe model's cursor input (its unique-where shape).

Remarks

after and before are mutually exclusive — a page runs in one direction, and passing both used to silently drop after. Pick a direction.

limit: null returns everything (from the after cursor when given). Combining limit: null with before is a compile error — "everything before the cursor, backwards, unbounded" is not something Prisma's negative take can express.

The default cursor is the record's id field, serialized with String and parsed back to a number when it is all digits (autoincrement ids) — a bigint once it exceeds Number.MAX_SAFE_INTEGER, so BigInt ids never lose precision — or kept as a string otherwise (uuid / cuid ids). Provide getCursor / parseCursor for composite keys, or when the selection omits id.


CursorPaginator

ts
type CursorPaginator<Results, Cursor> = object;

Defined in: packages/prisma/src/index.ts:299

What tryPaginate returns: a builder holding the query, consumed by withCursor.

Type Parameters

Type ParameterDescription
Results extends readonly unknown[]the (selection-narrowed) findMany payload.
Cursorthe model's cursor input (its unique-where shape).

Properties

PropertyModifierTypeDescriptionDefined in
withCursorreadonly(options) => AsyncResult<[Results, CursorPaginationMeta], InvalidCursor>Run the paginated query: the page and its metadata, or an InvalidCursor — the only modeled failure, since the cursor is the only part of the query that came from outside.packages/prisma/src/index.ts:305

PrismaQueryError

ts
type PrismaQueryError = 
  | UniqueConstraintViolation
  | ForeignKeyViolation
  | RecordNotFound;

Defined in: packages/prisma/src/index.ts:105

The full union of domain errors a Prisma query can surface.

Remarks

This is the RUNTIME-side union: qualifyPrismaError maps into it. Each try* method narrows the static type to the codes its operation can actually hit — a read hits none of them, so it is typed never.

Infrastructure failures are deliberately absent: they are defects, not values (see qualifyPrismaError). InvalidCursor is absent too — it belongs to pagination, not to a query.


TransactionClient

ts
type TransactionClient<C> = Omit<C, TxDenyList>;

Defined in: packages/prisma/src/index.ts:348

The client an interactive $tryTransaction callback receives — the extended client minus what a transaction cannot do.

Type Parameters

Type ParameterDescription
Cthe extended client, usually typeof db.

Remarks

Name the tx parameter of a helper factored out of a callback with this, rather than restating the deny list: $tryTransaction uses the very same alias, so the two cannot drift. Restating it by hand does drift silently — Omit of a key that does not exist is not an error, so a hand-copied list keeps compiling after the library's own list changes.

Not to be confused with Prisma's own generated Prisma.TransactionClient — that one is non-generic and, notably, does not remove $tryTransaction.

Example

ts
type Tx = TransactionClient<typeof db>;

const chargeFees = (tx: Tx, id: number) =>
  tx.invoice.tryUpdate({ where: { id }, data: { charged: true } });

db.$tryTransaction((tx) => chargeFees(tx, 1));

TransactionIsolationLevel

ts
type TransactionIsolationLevel = 
  | "ReadUncommitted"
  | "ReadCommitted"
  | "RepeatableRead"
  | "Snapshot"
  | "Serializable";

Defined in: packages/prisma/src/index.ts:285

The isolation levels Prisma accepts across databases, as a closed union.

Remarks

The schema-derived Prisma.TransactionIsolationLevel of a generated client is narrower (it lists only what YOUR database supports), but a shareable extension cannot name a generated type — this union at least rejects typos at compile time; a level your database does not support still fails at runtime as a defect (an unsupported level is a bug, not an outcome).

Variables

unthrownPrisma

ts
const unthrownPrisma: (client) => PrismaClientExtends<InternalArgs<{
}, {
  $allModels: {
     tryAggregate: AsyncResult<Result<T, A, "aggregate">, never>;
     tryCount: AsyncResult<Result<T, A, "count">, never>;
     tryCreate: AsyncResult<Result<T, A, "create">, CreateError>;
     tryCreateMany: AsyncResult<Result<T, A, "createMany">, CreateManyError>;
     tryCreateManyAndReturn: AsyncResult<Result<T, A, "createManyAndReturn">, CreateManyError>;
     tryDelete: AsyncResult<Result<T, A, "delete">, DeleteError>;
     tryDeleteMany: AsyncResult<Result<T, A, "deleteMany">, ForeignKeyViolation>;
     tryFindFirst: AsyncResult<Result<T, A, "findFirst">, never>;
     tryFindFirstOrThrow: AsyncResult<Result<T, A, "findFirstOrThrow">, RecordNotFound>;
     tryFindMany: AsyncResult<Result<T, A, "findMany">, never>;
     tryFindUnique: AsyncResult<Result<T, A, "findUnique">, never>;
     tryFindUniqueOrThrow: AsyncResult<Result<T, A, "findUniqueOrThrow">, RecordNotFound>;
     tryGroupBy: AsyncResult<Result<T, A, "groupBy">, never>;
     tryPaginate: CursorPaginator<Result<T, A, "findMany">, NonNullable<Args<T, "findMany">["cursor"]>>;
     tryUpdate: AsyncResult<Result<T, A, "update">, UpdateError>;
     tryUpdateMany: AsyncResult<Result<T, A, "updateMany">, UpdateManyError>;
     tryUpdateManyAndReturn: AsyncResult<Result<T, A, "updateManyAndReturn">, UpdateManyError>;
     tryUpsert: AsyncResult<Result<T, A, "upsert">, UpsertError>;
  };
}, {
}, {
  $tryTransaction: TryTransaction;
}>>;

Defined in: packages/prisma/src/index.ts:523

The Prisma Client extension. Apply it with $extends to add the try* methods to every model delegate, and $tryTransaction to the client.

Parameters

ParameterType
clientany

Returns

PrismaClientExtends<InternalArgs<{ }, { $allModels: { tryAggregate: AsyncResult<Result<T, A, "aggregate">, never>; tryCount: AsyncResult<Result<T, A, "count">, never>; tryCreate: AsyncResult<Result<T, A, "create">, CreateError>; tryCreateMany: AsyncResult<Result<T, A, "createMany">, CreateManyError>; tryCreateManyAndReturn: AsyncResult<Result<T, A, "createManyAndReturn">, CreateManyError>; tryDelete: AsyncResult<Result<T, A, "delete">, DeleteError>; tryDeleteMany: AsyncResult<Result<T, A, "deleteMany">, ForeignKeyViolation>; tryFindFirst: AsyncResult<Result<T, A, "findFirst">, never>; tryFindFirstOrThrow: AsyncResult<Result<T, A, "findFirstOrThrow">, RecordNotFound>; tryFindMany: AsyncResult<Result<T, A, "findMany">, never>; tryFindUnique: AsyncResult<Result<T, A, "findUnique">, never>; tryFindUniqueOrThrow: AsyncResult<Result<T, A, "findUniqueOrThrow">, RecordNotFound>; tryGroupBy: AsyncResult<Result<T, A, "groupBy">, never>; tryPaginate: CursorPaginator<Result<T, A, "findMany">, NonNullable<Args<T, "findMany">["cursor"]>>; tryUpdate: AsyncResult<Result<T, A, "update">, UpdateError>; tryUpdateMany: AsyncResult<Result<T, A, "updateMany">, UpdateManyError>; tryUpdateManyAndReturn: AsyncResult<Result<T, A, "updateManyAndReturn">, UpdateManyError>; tryUpsert: AsyncResult<Result<T, A, "upsert">, UpsertError>; }; }, { }, { $tryTransaction: TryTransaction; }>>

Remarks

Typing follows Prisma's documented $allModels pattern: this: T binds the concrete delegate, Prisma.Exact checks args, and Prisma.Result computes the payload — so select / include inference survives the wrap.

Example

ts
import { PrismaClient } from "./generated/prisma/client.ts";
import { unthrownPrisma } from "@unthrown/prisma";

const db = new PrismaClient({ adapter }).$extends(unthrownPrisma);

const users = db.user.tryFindMany({ select: { id: true } });
//    ^? AsyncResult<{ id: number }[], never>  — a read has no modeled failure

Functions

qualifyPrismaError()

ts
function qualifyPrismaError<D>(cause, defect): D | PrismaQueryError;

Defined in: packages/prisma/src/index.ts:171

Qualify a Prisma rejection — the runtime half of the bridge, and a ready-made qualify for any boundary you build yourself (raw SQL).

Type Parameters

Type Parameter
D

Parameters

ParameterTypeDescription
causeunknownthe rejected value from a Prisma query.
defect(cause) => Dthe defect helper the boundary injects (never import it).

Returns

D | PrismaQueryError

Remarks

The three P-codes that describe a domain outcome map to their tagged errors — P2002UniqueConstraintViolation, P2003ForeignKeyViolation, P2025 / P2018RecordNotFound.

Everything else is a defect, with the original cause preserved: a dropped connection, a pool timeout, a deadlock, an unmapped P-code, a malformed query, a client that could not start, an engine panic. None of those is something domain code branches on — they are logged and turned into a 500 at the edge, which is precisely what the defect channel is for. Modelling them would force every call site to carry an arm that duplicates its own defect arm.

A defect is not a crash: it flows through the pipeline untouched and is folded by match's defect handler like any other unmodeled failure.

Example

ts
// Pass it straight to a boundary — `defect` is injected for you.
const rows = fromPromise(db.$queryRaw`SELECT 1`, qualifyPrismaError);

Released under the MIT License.