Skip to content
VibORM
Esc
navigateopen⌘Jpreview
On this page

findUnique

Find a single record by unique identifier (primary key or unique scalar)

Basic Usage

// By primary key
const user = await client.user.findUnique({
  where: { id: "user_123" },
});

// By unique scalar
const user = await client.user.findUnique({
  where: { email: "alice@example.com" },
});

Compound Keys

For models with compound unique constraints:

// Schema
const membership = s.model({
  orgId: s.string(),
  userId: s.string(),
  role: s.string(),
}).id(["orgId", "userId"]);

// Query by compound key
const member = await client.membership.findUnique({
  where: {
    orgId_userId: {
      orgId: "org_1",
      userId: "user_1",
    },
  },
});

// With custom name
const membership = s.model({ ... })
  .id(["orgId", "userId"], { name: "membership_pk" });

const member = await client.membership.findUnique({
  where: {
    membership_pk: {
      orgId: "org_1",
      userId: "user_1",
    },
  },
});

Options

await client.user.findUnique({
  where: { ... },     // Required: unique identifier
  select: { ... },    // Optional: fields to return
  include: { ... },   // Optional: relations to include
});

select and include control the returned shape — see Selecting Fields.

findUniqueOrThrow

Throws if the record doesn’t exist:

const user = await client.user.findUniqueOrThrow({
  where: { id: "user_123" },
});
// Type: User (never null)
// Throws: NotFoundError if not found

Return Type

const user = await client.user.findUnique({
  where: { id: "user_123" },
});
// Type: User | null

// With select
const user = await client.user.findUnique({
  where: { id: "user_123" },
  select: { id: true, email: true },
});
// Type: { id: string; email: string } | null

Valid Unique Scalars

The where must name at least one unique discriminator — a scalar marked unique, or a complete compound constraint:

const user = s.model({
  id: s.string().id(),           // ✅ Primary key
  email: s.string().unique(),    // ✅ Unique scalar
  name: s.string(),              // ❌ Not a discriminator on its own
});

// Valid
await client.user.findUnique({ where: { id: "..." } });
await client.user.findUnique({ where: { email: "..." } });

// Invalid - TypeScript error, and a ValidationError at runtime
await client.user.findUnique({ where: { name: "..." } });

Narrowing a Unique Lookup

Alongside the discriminator you may pass ordinary non-unique scalar filters and AND / OR / NOT. The row must satisfy all of it:

// "the user with this id, but only if they're still active"
const user = await client.user.findUnique({
  where: {
    id: "user_123",
    status: "active",
    NOT: { deletedAt: { not: null } },
  },
});
// null when the id exists but the filter excludes it

The discriminator is still required — the extra filters narrow a unique lookup, they never replace it. A where with only filters is a ValidationError, not a scan.

The same where shape is accepted by update, delete and upsert. What “excluded” means there is per operation:

Operation Unique key matches, filter excludes
findUnique null
findUniqueOrThrow NotFoundError
update / delete NotFoundError, nothing written
upsert the create branch runs — and then hits the unique key that is already taken, so a UniqueConstraintError surfaces

Relation filters work here too, in the same position and with the same meaning as in findMany:

// "the account with this id, but only if it still has a live login"
const account = await client.account.findUnique({
  where: {
    id: 1,
    logins: { some: { status: "live" } },
    manager: { is: { active: true } },
  },
});

They narrow the row exactly as a scalar filter does — including on update, delete and upsert, per the table above. Self-relations are covered: on MySQL, where an UPDATE may not read the table it is mutating, the engine compiles the subquery through a derived table so the answer is the same everywhere.

To get-or-create a record atomically, use upsert instead of a findUnique + create pair.

Was this page helpful?