Skip to content
VibORM
Esc
navigateopen⌘Jpreview
On this page

upsert

Create a record if it doesn't exist, or update it if it does

upsert updates the record matching where if it exists, otherwise creates it — in a single atomic operation.

const user = await client.user.upsert({
  where: { email: "alice@example.com" },
  create: {
    email: "alice@example.com",
    name: "Alice",
  },
  update: {
    name: "Alice Updated",
  },
});

upsert is atomic and race-safe: concurrent upserts on the same key never fail with a duplicate error — one creates, the other updates.

What the create branch needs in create

To hand back the row it just inserted, upsert has to be able to name that row. It works this out from your create data alone — never from the where — and any one of these is enough:

  • every primary key field, as a literal;
  • every column of some unique constraint of the model (a .unique() column, or a whole compound unique);
  • nothing at all, when the model’s single primary key is a database-generated increment — the insert captures the value the database assigns.

Almost every model satisfies at least one of these without you doing anything. The exception is a model whose primary key is a generated compound key and which has no other unique constraint: nothing in create can name the inserted row, so upsert refuses with an UnsupportedOperationError instead of guessing. The refusal only fires when the create branch is actually taken — the same call updates normally when the row exists — and such a model cannot be written through create either (use createMany, or add a unique constraint).

Options

await client.user.upsert({
  where: { ... },       // Required: unique identifier
  create: { ... },      // Required: data for new record
  update: { ... },      // Required: data for existing record
  select: { ... },      // Optional: fields to return
  include: { ... },     // Optional: relations to include
});

With Relations

const user = await client.user.upsert({
  where: { email: "alice@example.com" },
  create: {
    email: "alice@example.com",
    name: "Alice",
    profile: {
      create: { bio: "New user" },
    },
  },
  update: {
    name: "Alice Updated",
    profile: {
      upsert: {
        create: { bio: "New profile" },
        update: { bio: "Updated profile" },
      },
    },
  },
  include: { profile: true },
});

Examples

User Settings

async function updateSetting(userId: string, key: string, value: string) {
  return client.userSetting.upsert({
    where: {
      userId_key: { userId, key },  // Compound unique
    },
    create: {
      userId,
      key,
      value,
    },
    update: {
      value,
    },
  });
}

Increment Counter

async function recordPageView(pageId: string) {
  return client.pageStats.upsert({
    where: { pageId },
    create: {
      pageId,
      views: 1,
      lastViewedAt: new Date(),
    },
    update: {
      views: { increment: 1 },
      lastViewedAt: new Date(),
    },
  });
}

vs Create + Update

Prefer upsert over a find-then-create/update sequence: it’s an atomic operation with no exposed race window. Use separate queries only when the two branches have genuinely different logic or you need to know which one ran.

Was this page helpful?