Use UUID in Rails 6 with PostgreSQL and Active Record

Tram Ho

UUID is an alternative primary key type for SQL database. It offers a number of benefits over the primary key in the standard integer style. Rails 6 released a new beta that introduced a new feature in ActiveRecord, making it easier to work with UUID primary keys. In this tutorial, we will dive into UUID with all their disadvantages and advantages.

Advantages of using UUID over integers

The UUID is a random string in a predefined format of the form like this:

UUID is superior to integer-based primary keys in many aspects. However, a warning might be the size of database indexes, but for tables without big data, you won’t notice the difference between integers and UUIDs.

Displaying non-public information in the URL

Primary key values ​​are commonly used publicly in URLs and API network logs. In turn, people can estimate the total number of application resources and the growth rate of the application.

Do you really want to disclose how many users are signing up for your service or how many products you are selling with public URLs like:

This problem can be mitigated by adding slugs, but these are only unique keys that overlap with additional maintenance requirements.

Switching to UUID results in URLs cannot reveal any confidential information:

Violation of access scope

It would be quite difficult to properly access resources in web applications with unusual business logic. Rails makes it so easy as

Instead of:

This example may seem obvious, but in other applications – when a user has different roles and complex logic for who can access something, it is not always possible to prevent it. completely similar errors.

In the above example, if the invoice ID is of the UUID type, the attacker will not be able to sequentially scan integer ID values ​​looking for security holes. This simple change makes a series of potential security flaws extremely difficult to exploit.

Anyway, I assert that using UUID will free you from restricting access to resources in your web application. However, it can save you in case similar security holes are detected in your project.

Independent of Frontend

The UUID primary keys allow frontend applications to independently create new objects, along with IDs, without having to talk to the backend. A unique ID can be generated with JavaScript code and the ability to duplicate existing objects is negligible.

This approach opens up a range of possibilities for frontend developers, for example, to create objects along with their links without calling the API.

Use UUID in Ruby on Rails application

You can create UUID with Ruby by:

To enable UUID in PostgreSQL, you need to create the following migration:

Do not forget to run

You can now configure new tables to use UUIDs as their primary keys:

Remember to set the correct foreign key data type on relational models. For this model case

The migration to create comments looks like this

If you want all your future models to use UUIDs for primary keys by default, you need to add the following file.

How do I convert the primary key of a table from an integer to a UUID?

Changing the type of the primary key is not easy. First, start by running a similar migration, which will create a new uuid column. Then rename the old id column to integer_id , un-set it as the primary key for the new uuid column after renaming to id .

I will not go into details about how to migrate associations because it will be different for the use cases. You need to follow the same steps to add a new UUID type column and based on the keyword value in addition to the old integer, you must reassign the correct UUID keys. This can be time consuming.

UUID arrangement problem

Prior to Rails 6, trying UUIDs in your application could be a bit frustrating. Obviously first and last – the ActiveRecord::Relation methods no longer work as expected, returning a seemingly random object from a collection.

Let’s look at an SQL query created by running User.last

Integer primary keys are generated sequentially. We can safely assume that the most recently created object will have the highest ID value.

In contrast, due to the completely random nature of the UUID, it is generated in a non-sequential order. PostgreSQL can still organize them by algorithm determination. That means a single UUID from the table will always have the first position when sorting. Unfortunately, it has nothing to do with it being compared to other UUID values ​​from the same table.

It results in a seemingly erroneous behavior of the first and last methods before Rails 6 because by default, they implicitly arrange relationships by ID values.

Changes in Rails 6

Rails 6 introduced a new implicit_order_value configuration option for the ApplicationRecord classes. You can use it like this:

With this setting, running User.last now creates the following query

The method then works as expected, even if it is using a non-sequential UUID for the primary key.

Using implicit_order_column may cause a potential error. In case of multiple identical created_at values, running the above query will return unknown results. The timestamp values ​​in Rails are accurate to milliseconds, so there may not be more than one object with the same creation time. But creating a series of objects is easy to happen.

Author’s contribution

The author of this post created a pull request related to the above mentioned problem and has been merged with Rails and will be released directly in version 6.0.2.

It modifies the implicit_order_column behavior to subordinate results of the query by primary key if it is available. It ensures the result of determination regardless of the potential duplicate values ​​in the implicit order column. An SQL query created by User.last now looks like this:

Use custom implicit arrangement in old Rails

You are stuck in an older version of Rails, but do you want to start using implicit_order_column right now? You can check out the author’s new gem that supports this feature. It is a bit difficult, but it is using it without problems in the Abot project based on the author’s Rails 5.

summary

Switching to UUID as the default primary key type in your Rails application is worth a look. I didn’t think of a single case, but could only use the integer type as the primary key instead of UUID. When creating a new model, you cannot imagine all the possible business logic requirements that it will handle. Using UUID in the first place can help you reduce the cumbersome migration in the future.

Article translated from source . Thank you for reading.

Share the news now

Source : Viblo