Better Programming

Advice for programmers.

Follow publication

Empowering a Rails Application With UUID as Default Primary Key

Mirza Sehovic
Better Programming
Published in
4 min readApr 9, 2020

--

Photo by Jessica Ruscello on Unsplash

I assume that most of you are familiar with what the UUID is and what it looks like. Therefore, I’ll skip the introduction and jump straight to the point.

Motivation

Obfuscate information in the URL

If we use an incrementing integer as ID, we would expose information in the URL that can be inferred from the outside. The total number of records might be one of the things that we want to keep secret.

We would need to implement and maintain additional mechanisms to achieve that. On the other side, by using UUID, we don’t need to worry about that as it’s handled out of the box.

# incrementing integer VS universally unique identifierlocalhost:3000/articles/36
localhost:3000/articles/fc9b45f6–94eb-47b9-b029–3bc55f810a35

Additional security layer

Usage of UUID as a primary key can also act as an additional security layer. It can prevent a scenario where, by guessing the id in the URL, a malicious attacker could attempt to gain access to data. The opposite of an incrementing integer, the UUID is incredibly difficult to guess.

However, I strongly endorse implementing permission-based authorization first. Simple possession of the particular UUID should not mean granted access at all.

Universally unique at its finest

I know it sounds scary but, with the UUID approach, you can assign the object IDs on the client-side or even in other systems. Therefore, the overhead of API calls can be eliminated. This can be a useful thing in batch actions in particular.

When generated according to the standard methods, UUIDs are, for practical purposes, unique.

Their uniqueness does not depend on a central registration authority or coordination between the parties generating them, unlike most other numbering schemes. While the probability that a UUID will be duplicated is not zero, it is close enough to zero to be negligible.

Usage Guide

Intro

The process and difficulties of making this work depend on what database you’ll use. My personal first choice is PostgreSQL as I consider it to be the best option. If you’re using PostgreSQL as well then this short guide is for you.

It is a straightforward process and has few performance costs. Setting this up might be a totally different game for other databases. I’m sure that there are plenty of resources on the internet where you can learn more. It might be a complicated process that is not worth the prize. Good luck in any case!

With that being said, here are the steps for setting up UUID as the primary key for the PostgreSQL database.

Create a migration

To enable UUID usage in PostgreSQL, you’ll need to create and run the following migration.

# frozen_string_literal: trueclass EnableUuid < ActiveRecord::Migration[6.0]
def change
enable_extension 'pgcrypto'
end
end

Implement initializer

At this point, we would need to configure new tables to use UUID for their primary keys as it’s not handled by default.

To avoid that repetitive work, we’ll create and run the following initializer. That way, when generating the migrations, the UUID will be used as a primary key by default.

config/initializers/generators.rb

# frozen_string_literal: trueRails.application.config.generators do |g|
g.orm :active_record, primary_key_type: :uuid
end

Setting up model relations

The one thing we should keep in mind is that we need to be explicit while defining model relations on the database level. As UUID is used as the primary key, we would need our foreign keys to be of the same type as well. Let’s take a look at the following example.

Let’s imagine that we want to have Article and Author models and that they should be in a one-to-many relation.

For the Article model, we would want it to belong to one Author. Therefore, we’ll need to have a reference defined in our migration. The additional step then usually is that we’ll need to explicitly define that UUID is the type of the foreign key.

We can do that by adding the type: :uuid part to the reference definition. The migration for creating the articles table should look something like this.

Handling the pitfalls

Besides its numerous advantages, the UUID as a primary key approach has its pitfalls, which are addressed by the community as well. One of them that is not debatable is that the .first and .last ActiveRecord::Relation would no longer work as expected.

As we already know, those methods use the id column for sorting. Having the UUID value in that column instead of the usual incrementing integer would result in surprising behavior.

However, Rails 6 introduced the possibility of overriding the column used for implicit ordering.

We can use that ability in our base abstract model and make use of the created_at field. Therefore, the .first and .last will work properly again for all models that inherit from it. Here is what it should look like.

# frozen_string_literal: trueclass ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
self.implicit_order_column = 'created_at'
end

That should be the last step to set up your application to use UUID as the primary key. Feel free to reach out with any questions or concerns that you might have. Cheers!

Disclaimer

Using UUID as a primary key in a brand new project is something that can be a good idea. It’s up to you to decide.

However, I advise on avoiding the migration of live projects unless it’s really necessary. That could be a challenging task and the stakes are high. Therefore, it should be treated with special care.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Mirza Sehovic
Mirza Sehovic

Written by Mirza Sehovic

Crafting software since 2010, Master’s degree in Engineering, Ruby on Rails specialist

Write a response