# Convert database constraints errors into validation errors

**URL:** <https://discuss.rubyonrails.org/t/convert-database-constraints-errors-into-validation-errors/84564>\
**Category:** rubyonrails-talk\
**Created:** [January 7, 2024, 5:16pm UTC](https://discuss.rubyonrails.org/t/convert-database-constraints-errors-into-validation-errors/84564 "2024-01-07T17:16:26Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Fire-Dragon-DoL](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/fire-dragon-dol/32/11081_2.png) [@Fire-Dragon-DoL](https://discuss.rubyonrails.org/u/Fire-Dragon-DoL)\
**Post date:** [January 7, 2024, 5:16pm UTC](https://discuss.rubyonrails.org/t/convert-database-constraints-errors-into-validation-errors/84564/1 "2024-01-07T17:16:26Z")

</div>

Hi, I noticed that 90% of the times I create a database constraints, I want that error to become an ActiveRecord validation error. The remaining 10% I usually want the error to remain an exception and handle that exception in some way (usually, retry).

At this point I thought “why not integrating this support directly in ActiveRecord”? What followed was a couple of hard coding sessions as I wrote [iry](https://github.com/Fire-Dragon-DoL/iry), a **terrible** proof-of-concept library that allows to declare database constraints by name and convert them to validation errors, when these arise.  
Why terrible? It has to resort to some monkey patching to work, since there is no public API to achieve what I was trying to do.  
Notice that the monkey-patched version patches a method that hasn’t changed since Rails 3, but there is no guarantee it will not change.

The gem currently supports only postgres

```sql
create extension if not exists "pgcrypto";

create table if not exists users (
    id uuid primary key default gen_random_uuid(),
    unique_text text unique not null default gen_random_uuid()::text
    created_at timestamp(6) not null,
    updated_at timestamp(6) not null
);

```

```ruby
class User < ActiveRecord::Base
  include Iry

  unique_constraint :unique_text
end

user = User.create!(unique_text: "some unique text")
fail_user = User.new(unique_text: "some unique text")

success = Iry.save(fail_user)

success #=> false
fail_user.errors.details.fetch(:unique_text) #=> [{error: :taken}]

```

Using `fail_user.save` (or `save!`) would run the code without activating the monkey-patch, this is to avoid breaking existing code, while `Iry.save` (and `Iry.handle_constraints`) set a thread-level variable to activate the monkey-patch.

Currently it supports:

- unique constraints
- check constraints
- foreign key constraints
- exclusion constraints

What do you think about this?  
Would there be any interest in integrating this in ActiveRecord?

---

<div class="post-metadata">

**Author:** ![byroot](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/byroot/32/780_2.png) [@byroot](https://discuss.rubyonrails.org/u/byroot)\
**Post date:** [January 8, 2024, 4:18pm UTC](https://discuss.rubyonrails.org/t/convert-database-constraints-errors-into-validation-errors/84564/2 "2024-01-08T16:18:36Z")

</div>

Thanks for sharing.

There is also [GitHub - Shopify/activerecord-rescue\_from\_duplicate: Ruby gem to rescue from MySQL, PostgreSQL and Sqlite duplicate errors](https://github.com/Shopify/activerecord-rescue_from_duplicate) and [GitHub - wvanbergen/activerecord-databasevalidations: Add validations to your ActiveRecord models based on your database constraints.](https://github.com/wvanbergen/activerecord-databasevalidations).

The difficulty with this as you certainly noticed is to support multiple databases.

Upstreaming this sort of capability into Rails is something that is on the radar of my team, so yes, there is interest in integrating this in Active Record for Rails 7.2-ish.

---

<div class="post-metadata">

**Author:** ![Fire-Dragon-DoL](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/fire-dragon-dol/32/11081_2.png) [@Fire-Dragon-DoL](https://discuss.rubyonrails.org/u/Fire-Dragon-DoL)\
**Post date:** [January 9, 2024, 1:38am UTC](https://discuss.rubyonrails.org/t/convert-database-constraints-errors-into-validation-errors/84564/3 "2024-01-09T01:38:12Z")

</div>

Amazing, thank you for letting me know. I’m happy to contribute if you need a hand with it, but I am not a Rails contributor, so I’m afraid I might just be in the way.

The major challenge I faced was dealing with accepts\_nested\_attributes\_for, since there is no way to determine the order in which the records will be written
