Ian Bradbury

August 19, 2021

How to configure Rails Active Storage to use UUIDs for database primary keys

Scenario = you'd love to use Active Storage but you need storage blob uri's to be non-guessable.  Or, maybe you already have an app that uses UUIDs and you're looking to add ActiveStorage to the application.  Read on....

I will assume that you're running Rails v6+ and Postgres v9.1+ that has already been configured to use UUIDs.  (You can read how to configure Postgres for UUIDs here)

Please note : this post was originally written/published October 2020, I am sure the migrations detailed below will have changed.  Just be aware that you'll need to adjust to modern times on your own.

So you follow the Active Storage installation guide.  Here.  And it just works.  Yay!

But then you realise.  Those table ID columns are all auto increment integers.  Boo!

So how to resolve this?

Turns out's it's prety simple - but only if you make the fix as you install Active Storage.  If you've already installed Active Storage and run the database migrations then I think you'll have a job on your hands to revise the schema for UUIDs.

Implement Active Storage + UUIDs

It's all pretty simple and some might say not even worth a blog post.  The solution is to manually adjust the database migration files that are generated as part of the Active Storage installation process.  It's really not rocket science.

Step 1:

Install Active Storage and generate the database migrations - do not run the migrations.

rails active_storage:install

These are the vanilla migrations:

  def change
    create_table :active_storage_blobs do |t|
      t.string   :key,        null: false
      t.string   :filename,   null: false
      t.string   :content_type
      t.text     :metadata
      t.bigint   :byte_size,  null: false
      t.string   :checksum,   null: false
      t.datetime :created_at, null: false

      t.index [ :key ], unique: true
    end

    create_table :active_storage_attachments do |t|
      t.string     :name,     null: false
      t.references :record,   null: false, polymorphic: true, index: false
      t.references :blob,     null: false
      t.datetime :created_at, null: false

      t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
      
      t.foreign_key :active_storage_blobs, column: :blob_id
    end
  end
end



And these are the adjusted migrations:

  def change
    create_table :active_storage_blobs, id: :uuid do |t|
      t.string   :key,        null: false
      t.string   :filename,   null: false
      t.string   :content_type
      t.text     :metadata
      t.bigint   :byte_size,  null: false
      t.string   :checksum,   null: false
      t.datetime :created_at, null: false
  
      t.index [ :key ], unique: true
    end
  
    create_table :active_storage_attachments, id: :uuid do |t|
      t.string :name, null: false
      t.references :record, type: :uuid, null: false, polymorphic: true, index: false
      t.references :blob, type: :uuid, null: false
      t.datetime :created_at, null: false
  
      t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
      
      t.foreign_key :active_storage_blobs, column: :blob_id
    end
  end
end

Once you've updated the migrations.

rails db:migrate

Job done.  You now have Active Storage and a much more secure application.  

Good work.  Pat yourself not the back.  🙌🏻

Links: