How to handle Postgres errors inside try catch (with Drizzle)? #916
Replies: 6 comments 5 replies
-
Does anyone know ? I'm stumbling upon the same situation right now. |
Beta Was this translation helpful? Give feedback.
-
I noticed that the error message comes from the database library I use in drizzle. In my case, it is "pg". |
Beta Was this translation helpful? Give feedback.
-
Is this concept possible in the first place? I'm running a try catch inside a transaction to watch for a db conflict error, but the entire transaction is throwing this error while the closure in the transaction keeps on running. For example: await db.transaction(async (tx) => {
let retryCount = 0;
while (retryCount < 10) {
try {
await tx.insert()
} catch (error) {
const queryError = error as QueryError;
if (
typeof queryError.code === "string" &&
queryError.code === "23505"
) {
// Unique violation (Postgres specific error code)
// Regenerate slug by appending a unique identifier (e.g., retryCount)
slug = slugify(knowledge.title) + "-" + (retryCount + 1);
retryCount++;
} else {
throw error; // Re-throw other errors
}
}
}
} But once the first error is thrown, the entire transaction seems to return, while the code just continues to execute. |
Beta Was this translation helpful? Give feedback.
-
You can use the errors thrown by postgres, though that does have some of its own problems. import pg from 'postgres';
const isUniqueConstraintError = (error: unknown): boolean => {
/** https://github.com/porsager/postgres/pull/901 */
// eslint-disable-next-line import/no-named-as-default-member
return error instanceof pg.PostgresError && error.code === '23505';
};
export default isUniqueConstraintError; import isUniqueConstraintError from '@/lib/database/errors/isUniqueConstraintError';
// Insert item into db
try {
// Insert item into the database with the given data and max position
await db.insert(cars).values({ ...data });
} catch (error) {
// Check if the error is a unique constraint error
if (isUniqueConstraintError(error)) {
// handle however you want....
} else {
// Handle other errors
....
}
} Ofcourse, you can use the PostgresError to get more details. |
Beta Was this translation helpful? Give feedback.
-
This is the weird way that I've worked around this. Drizzle will throw a import { DrizzleQueryError } from "drizzle-orm/errors";
import postgres from "postgres";
export const isUniqueConstraintError = (error: unknown): boolean => {
/** https://github.com/porsager/postgres/pull/901 */
// eslint-disable-next-line import/no-named-as-default-member
if (!(error instanceof DrizzleQueryError)) {
return false;
}
const cause = error.cause;
if (!cause || typeof cause !== 'object') {
return false;
}
return 'code' in cause && (cause as postgres.PostgresError).code === "23505";
}; |
Beta Was this translation helpful? Give feedback.
-
Background:Drizzle depends on database drivers to perform actual operations on the database. For Now the problem is, since drizzle does not perform the database operations itself it gets success/failure information about the operation through errors/messages returned by the underlying driver. The drizzle's session layer is driver agnostic. So the database interation code looks like this: try {
return await query();
}
catch (e) {
throw new DrizzleQueryError(queryString, params, e as Error);
} Basically, it just tries to run the query using the database driver provided, and in case, any error is thrown at a lower level, it just:
There are several problems with this approach:
Though I think this is a difficult problem, because:
I think the best approach out here, is to define a common interface for all Errors (can expand DrizzleQueryError to include more fields). And then, in the catch block above, try to find out what went wrong and populate as must information we have before we throwing the Temporary solutionI'm using Upon failure, it throws a So I build the following solution, to detect errors: import { DrizzleQueryError } from "drizzle-orm";
import { DatabaseError } from "pg-protocol";
// Relevant codes from: https://www.postgresql.org/docs/current/errcodes-appendix.html
export enum OPSTATUS {
SUCCESS,
// integrity violations
FOREIGN_KEY_VIOLATION=23503,
UNIQUE_VIOLATION=23505,
CHECK_VIOLATION=23514,
NOT_NULL_VIOLATION=23502,
// transaction failure
INVALID_TRANSACTION_STATE=25000,
// connection failure
CONNECTION_DOES_NOT_EXIST=8006,
CONNECTION_FAILURE=8006,
// other
UNKNOWN_FAILURE=-1,
}
// ...
try {
const insertedUser = await db.insert(usersSchema).values(user).returning();
if ( insertedUser[0] === undefined ) {
throw new Error("Unknown Failure"); // handled below
}
return insertedUser[0];
}
catch (err: any) {
// if error has not originated from pg driver, no useful information can be extracted
if ( !(err instanceof DrizzleQueryError) || !(err.cause instanceof DatabaseError) ) {
return {
success: false,
status: OPSTATUS.UNKNOWN_FAILURE,
message: "Unknown Failure"
}
}
// ...
// try to find out what went wrong?
switch (errCode) {
case OPSTATUS.UNIQUE_VIOLATION: {
recommendedHttpResponseCode = StatusCodes.CONFLICT;
message = "User already exists";
break;
}
case OPSTATUS.CONNECTION_FAILURE: {
recommendedHttpResponseCode = StatusCodes.SERVICE_UNAVAILABLE;
message = "Broken connection to database server";
break;
}
// and so on..
}
// return approapriate response
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm trying to understand how to properly type my errors when Drizzle throws due to a Postgres error.
Example, when I attempt to insert a number into a string column, I was thinking within my catch block I could use:
Should I just use PostgresError?
Thanks!
Beta Was this translation helpful? Give feedback.
All reactions