# Prisma & Drizzle Outbox Pattern: Atomic Database Transactions for Email

_By Tayyab Mughal, Founder & AI Chief · 10 August 2026 · 2 min read_

> How to guarantee zero lost emails during database writes using the Transactional Outbox pattern with Prisma ORM, Drizzle, and Postgres.

Never lose a confirmation email when your API crashes midway through a transaction. Learn how to implement the Outbox pattern with Prisma and Drizzle ORM.

## The Dual-Write Problem in Transactional Email

If you create a user in your database and then immediately call an email API, a network failure or server crash leaves your system in an inconsistent state: the user exists, but the verification email was never sent.

The Transactional Outbox pattern solves this by writing the email job into an outbox_messages database table inside the exact same atomic database transaction.

## Prisma Implementation with Interactive Transactions

Write user records and outbound email jobs atomically using prisma.$transaction().

```typescript
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export async function registerUserWithOutbox(email: string, name: string) {
  return await prisma.$transaction(async (tx) => {
    // 1. Create user record
    const user = await tx.user.create({
      data: { email, name },
    });

    // 2. Atomically insert email job into outbox
    await tx.outboxMessage.create({
      data: {
        to: email,
        subject: 'Verify your email address',
        body: `Hello ${name}, please click here to verify your account.`,
        status: 'PENDING',
      },
    });

    return user;
  });
}

// Background poller picks up pending messages with SKIP LOCKED
export async function pollOutboxQueue() {
  const pending = await prisma.outboxMessage.findMany({
    where: { status: 'PENDING' },
    take: 10,
  });

  for (const msg of pending) {
    const res = await fetch('https://api.sadasend.com/v1/emails', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.SADASEND_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ to: msg.to, subject: msg.subject, text: msg.body }),
    });

    if (res.ok) {
      await prisma.outboxMessage.update({
        where: { id: msg.id },
        data: { status: 'SENT', sentAt: new Date() },
      });
    }
  }
}
```

## Key Benefits of the Outbox Pattern

- 100% Guaranteed Delivery: Even if the email API is down, messages are safely queued in Postgres.
- Zero Phantom Emails: If the database transaction rolls back, no email is ever sent.
- Idempotent Processing: Safe retries using Postgres FOR UPDATE SKIP LOCKED locks.

---

_Tags: Prisma, Drizzle, Postgres, Architecture_
