Working with UUIDs in Rails

Working with UUIDs in Rails

Let's see how to work with uuids in Rails. First of all, have in mind this is not the default in Rails, but it should be fine/easy to implement.

Use it in both sides

We needed to use UUIDs in one of our projects. Our front-end application was generating UUIDs and we wanted to use it also in our back-end.

Using UUIDs helps us not having a centralized id generation. We don't need to rely on just one place to create resources.

Imagine you have this scenario: a react application creating and editing resources and a Rails API. The React Application communicates with the back-end. It needs to have local IDs before persisting data, in order to reference relationships ahead of time, and it creates a large graph of objects at once. Should you have different IDs for both sides? No, you don't need that. You can generate UUIDs in JavaScript and use them for both sides.

On Rails

Enable the extension pgcrypto. Previously the extension you needed was uuid-ossp.

So, depending in which Postgres version you're using, you can create a migration to enable this extension. In our case, we will enable both.

rails migration EnableUuidExtension

And our migration will look like:

class EnableUuidExtension < ActiveRecord::Migration[5.1]
  def change
    enable_extension 'uuid-ossp'
    enable_extension 'pgcrypto'
  end
end

The next thing is to use the uuid in one of our models. We can do that in two ways:

1.Using id: false

class CreatePerson < ActiveRecord::Migration[5.1]
  def change
    create_table :people, id: false do |t|
      t.uuid :id,  null: false
      t.string :name
      t.timestamps
    end
    add_index :people, :id, unique: true
  end
end

In this case, we are using id:false in our create_table and we are setting the id as uuid. We are also creating the index here for our id and setting the id to not be null. In order to create the uuid, you could create a before_create and every time it creates a new object you could set the uuid.

2.Using id: uuid

class CreatePeople < ActiveRecord::Migration[5.1]
  def change
    create_table :people, id: :uuid do |t|
      t.string :name
      t.timestamps
    end
  end
end

In this case, we are passing id: :uuid in the create_table and we don't need to pass the id inside of the block. Doing that, it generates a uuid every time you create a new object. This should be a normal way to use uuids.

If we try to create a new person, you can see it generates a new uuid for that:

irb(main):002:0> p = Person.create
   (0.2ms)  BEGIN
  SQL (2.2ms)  INSERT INTO "people" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2017-07-14 03:06:32.697148"], ["updated_at", "2017-07-14 03:06:32.697148"]]
   (1.9ms)  COMMIT
=> #<Confirmation id: "886bb121-bd76-4199-9dcb-ce40e0164edf", name: nil, created_at: "2017-07-14 03:06:32", updated_at: "2017-07-14 03:06:32">

Checking

Now, imagine you have set up uuids in your Rails application, and you want to use it.

And you try to create a new Person and you pass a uuid 123:

irb(main):001:0> p = Person.new(id: '123')
=> #<Person id: nil, name: nil, created_at: nil, updated_at: nil>
irb(main):002:0> p.id
=> nil

Doing that, you can see the person didn't get the id. Why did this happen?

This is because ActiveRecord checks if the id matches the regular expression. As we are using uuids, it should be a real uuid and not 123. Thanks Rafael Sales to point me out this!

Right now, if we try to pass a uuid to our object, it should work.

irb(main):002:0> p.id
=> nil
irb(main):003:0> p.id = SecureRandom.uuid
=> "e3e0f7c2-8a85-42b9-a31d-867504cb6eb5"
irb(main):004:0> p.id
=> "e3e0f7c2-8a85-42b9-a31d-867504cb6eb5"

I hope this post helps you see how simple it is to use uuids in Rails.