/drizzle & /pg

@thourum/fault/drizzle wraps query promises, while @thourum/fault/pg classifies PostgreSQL and Node connection error codes.

safeDb

safeDb<T>(dbPromise: Promise<T>): ResultAsync<T, Fault>

const inserted = safeDb(
  db.insert(posts).values({ authorId, text, imageKey }).returning(),
)

safeDb maps a rejected Drizzle query through DatabaseError and leaves successful values unchanged.

Tags produced: the tags from DatabaseError and parsePgError.

DatabaseError

DatabaseError(cause: unknown): Fault

const fault = DatabaseError(error)

DatabaseError classifies a PostgreSQL/Node error directly or from a Drizzle error's cause, retaining the original outer error as the Fault's cause. A DrizzleQueryError uses its inner driver's message (or Failed query when absent), with query and params in metadata—not the message. A TransactionRollbackError without a recognized pg cause gets TRANSACTION_ROLLBACK_ERROR and its own message. Other errors keep the driver message (String(cause) for non-Errors) and get DATABASE_ERROR; no synthetic label is prepended. Driver messages can be sensitive, so do not send raw Fault messages straight to end users.

Tags produced: DATABASE_ERROR, TRANSACTION_ROLLBACK_ERROR, or any tag from parsePgError.

isPostgresError

isPostgresError(error: unknown): error is PgDatabaseError

if (isPostgresError(error)) {
  return parsePgError(error)
}

The check is deliberately duck-typed: the value must be an Error, contain a code property, and have a string code, so Node errors such as ECONNREFUSED are accepted too.

Tags produced: none.

parsePgError

parsePgError(error: PgDatabaseError | Error): Fault

try {
  await db.insert(posts).values(post).returning()
} catch (error) {
  const fault = isPostgresError(error)
    ? parsePgError(error)
    : DatabaseError(error)
}

parsePgError adds pgCode, pgDetail, pgTable, pgColumn, pgConstraint, and pgSchema metadata for coded errors, then maps codes as follows. Unknown codes and non-pg errors keep the real driver message; code-less errors matching /Connection terminated/i are connection failures.

Tags produced: UNIQUE_CONSTRAINT_ERROR, FOREIGN_KEY_ERROR, VALIDATION_ERROR, DATABASE_ERROR, CONNECTION_ERROR, or TRANSACTION_ROLLBACK_ERROR.

SQLSTATE or error codeTag
23505UNIQUE_CONSTRAINT_ERROR
23503FOREIGN_KEY_ERROR
23502VALIDATION_ERROR
23514VALIDATION_ERROR
42P01DATABASE_ERROR
42703DATABASE_ERROR
57014DATABASE_ERROR
ECONNREFUSEDCONNECTION_ERROR
ENOTFOUNDCONNECTION_ERROR
ETIMEDOUTCONNECTION_ERROR
ECONNRESETCONNECTION_ERROR
EPIPECONNECTION_ERROR
EAI_AGAINCONNECTION_ERROR
Code-less Connection terminated messageCONNECTION_ERROR
40001TRANSACTION_ROLLBACK_ERROR
40P01TRANSACTION_ROLLBACK_ERROR
Any other code beginning with 08CONNECTION_ERROR
Every other codeDATABASE_ERROR

Known codes retain their specific messages. Unique, foreign-key, and not-null faults also include the inferred field in metadata when available. A standalone Drizzle TransactionRollbackError retains the default HTTP status 500.