Respondendo 409 com a constraint certa¶
O banco diz por que recusou a escrita — e diz na prosa do driver, não na da sua
aplicação. parseIntegrityError lê essa prosa de volta para uma estrutura:
import { BaseRepository, parseIntegrityError } from "tempest-db-js";
try {
await users.create({ email, tenantId });
} catch (error) {
const failure = parseIntegrityError(error, User); // (1)!
if (failure?.violation === "unique" && failure.columns.includes("email")) {
return json({ code: "EMAIL_TAKEN", field: "email" }, { status: 409 });
}
throw error; // (2)!
}
- O modelo é opcional; passá-lo traduz o nome do banco de volta para a propriedade
(
idempotency_key→idempotencyKey) em quem usanaming = "snake_case". - Não é violação de integridade ⇒
null. Reerguer é o certo: engolir aqui esconderia erro de sintaxe, deadlock, queda de conexão.
O que volta¶
| Campo | Traz |
|---|---|
violation |
"unique" · "foreignKey" · "notNull" · "check" · "exclusion" |
constraint |
nome, quando o banco reporta (PostgreSQL sim, SQLite não) |
table |
tabela, quando reportada |
columns |
colunas cobertas — todas numa constraint composta |
detail |
a mensagem crua do driver, para o log |
O que cada banco entrega¶
O mesmo erro chega de duas formas diferentes, e a diferença não é cosmética:
MySQL devolve null
O MySQL está fora do escopo ativo do projeto, então um ER_DUP_ENTRY devolve null
em vez de um palpite. null significa "não sei", não "não foi violação" — trate
reerguendo, como qualquer erro desconhecido.
Constraint composta vem inteira
columns traz todas as colunas de uma unique composta, que é exatamente o que
decide se a mensagem para o usuário fala de e-mail ou do par (e-mail, tenant).
Recapitulando¶
parseIntegrityError(error, Model?)→IntegrityFailure | null.- Segue a cadeia de
cause, então oQueryExecutionErrordo pacote não atrapalha. - Passe o modelo para receber nome de propriedade em vez de nome de coluna.
null⇒ reerga o erro. 🚀