sqlx icon indicating copy to clipboard operation
sqlx copied to clipboard

[question] Convert errors to user friendly errors

Open itmecho opened this issue 5 years ago • 4 comments

Is there a best practice method of converting a sqlx::Error::Database to a user friendly message. For example, when writing HTTP API, I have a create user route. When there's already a user with the email address given, the error returned is the constraint error:

duplicate key value violates unique constraint "users_email_key"

Is there an easy way to map these errors to a more user friendly error, something like "A user with that email address already exists"? This is the best I've come up with so far but it seems a bit verbose/awkward:

            ...
            .execute(db)
            .await
            .map_err(|e| {
                match e {
                    sqlx::Error::Database(e) => {
                        if let Some("23505") = e.code() {
                            return UserError::EmailTaken;
                        }
                    }
                    _ => {}
                };
                UserError::General(e)
            })?;

itmecho avatar Aug 26 '20 21:08 itmecho

This is perhaps slightly better:

match e {
    sqlx::Error::Database(e) if e.column_name() == Some("email") => {
        UserError::EmailTaken
    }
    _ => UserError::General(e),
}

I think we could stand to improve the ergonomics here but I'm not sure how I'd want it to handle.

abonander avatar Aug 26 '20 21:08 abonander

Ah yea that is cleaner, however when I debug log the e, the column_name shows as None 😕

itmecho avatar Aug 26 '20 22:08 itmecho

Oh, it might be e.constraint_name() == Some("email_unique") or something like that. You can switch it back to matching on the error code if you find that more reliable.

abonander avatar Aug 27 '20 00:08 abonander

just an update i think as of today this should look something like:

match err {
                sqlx::Error::RowNotFound => Err(Error::UserNotFound.into()),
                sqlx::Error::Database(err)
                    if err.constraint() == Some("username_key") =>
                {
                    Err(Error::UsernameTaken.into())
                }
                _ => Err(err.into()),
            }

wileymc avatar Jun 05 '22 18:06 wileymc