How to use ULID primary keys in Rails

How to use ULID primary keys in Rails
Do not index
Do not index
URL
ULID (Universally Unique Lexicographically Sortable Identifier) is a new type of identifier that has several benefits when used as primary keys for database tables. One of the main benefits is that ULIDs are guaranteed to be unique across space and time. This is because they are generated using a combination of a timestamp and a random number, which ensures that no two ULIDs will ever be the same.
Another benefit of using ULIDs is that they are lexicographically sortable. This means that they can be sorted based on the order in which they were created, which can be useful when querying data in a database.
ULIDs are also compact and easy to generate, making them well suited for use in distributed systems. They can be generated without a centralized authority, which eliminates the need for a centralized system to assign unique identifiers.
Finally, ULIDs are human-readable and can be encoded in a variety of formats, such as Base32 or Base64, which makes them easy to share and transfer.
In short ULIDs are short, unique, lexicographically sortable, and can be generated easily, thus making them a great fit for primary keys in database tables.

Use an ULID for ActiveRecord primary key’s

Add ulid-ruby to your Gemfile:
gem "ulid-ruby", require: "ulid"
Add a concern to generate a ULID for your active_record models:
module UlidPk
  extend ActiveSupport::Concern

  included do
    before_create :set_ulid
  end

  def set_ulid
    self.id = ULID.generate
  end
end
Include the concern in your application_record.rb:
class ApplicationRecord < ActiveRecord::Base
  include UlidPk
  primary_abstract_class
end
When you generate new models the create_table migration needs a few adjustments as well. The default auto increment id needs to be disabled. This can be done by passing in the id: false attribute to the create_table method. For example: create_table :users, id: false do |t|
The last step that’s needed is to create the id that will hold the ULID like so: t.binary :id, limit: 16, primary_key: true.
class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users, id: false do |t|
      t.binary :id, limit: 16, primary_key: true
      t.string :email, null: false

      t.timestamps
    end

    add_index :users, :email, unique: true
  end
end
When adding associations you will have to make sure to specify the foreign key as a binary to make sure it’s the same format at the primary key, for example:
t.references :user, type: :binary, null: false, foreign_key: {on_delete: :cascade}
 
Sources and related blog articles:
spec
Github
spec
Owner
ulid
Updated
Aug 29, 2023
ulid-ruby
Github
ulid-ruby
Owner
abachman
Updated
May 23, 2023
ulid
Github
ulid
Owner
rafaelsales
Updated
Aug 30, 2023

Written by