# How to create auto incrementing primary key with prefix in RoR application.

Recently, while experimenting with Stripe, I noticed that objects have IDs or identifiers with prefixes. Below, I've listed a few examples. You can find more in this gist: [https://gist.github.com/fnky/76f533366f75cf75802c8052b577e2a5](https://gist.github.com/fnky/76f533366f75cf75802c8052b577e2a5)

| Prefix | Description | Notes |
| --- | --- | --- |
| cus\_ | Customer ID | Identifier for a Customer object. |
| pk\_live\_ | Live public key | Public key in a live environment. |
| pk\_test\_ | Test public key | Public key in a test environment. |

I found this extremely useful. You see the ID, and right away, you can identify the object it belongs to. Here, I found a quote from Stripe's co-founder:

> They're randomly generated by our Ruby application code. We use the `ch_`\-style prefixes because we find it really useful to be able to immediately recognize the type of an ID when looking at logs or stacktraces.

[https://qr.ae/pKjsuo](https://qr.ae/pKjsuo)

In this article, I will show you how to implement this in a RoR application. We will get ids like: `usr_1 urs_2 usr_3`. Let's dive into the implementation.

# Implementation

First, let's create a migration:

```ruby
rails g migration create_users email
```

This will generate our migration file:

```ruby
class CreateUsers < ActiveRecord::Migration[7.1]
  def change
    create_table :users do |t|
      t.string :email

      t.timestamps
    end
  end
end
```

By default all tables will be crated with primary\_key :id. We want to change this, so we add id: false option

```ruby
create_table :users, id: false do |t|
  t.string :email

  t.timestamps
end
```

Now we need to attach primary key to the table:

```ruby
create_table :users, id: false do |t|
  t.string :id, null: false, primary_key: true, default: -> { "'usr_'||nextval('usr_seq_id'::regclass)::TEXT" }
  t.string :email

  t.timestamps
end
```

Let's look closer on the implementation:

```ruby
"'usr_'||nextval('usr_seq_id'::regclass)::TEXT"
```

If we don't attach an ID when creating a user, then this code will be called from the default. Here, we call a PostgreSQL sequence that will automatically generate our ID. Before that, we added the prefix `usr_`. However, we haven't created a sequence yet. So let's do that. Before creating a table, we need to create a sequence. I couldn't find an Active Record function to accomplish this, so we'll use the `connection.execute` method instead.

This is how it will look like:

```ruby
connection.execute('CREATE SEQUENCE usr_seq_id')
create_table :users, id: false do |t|
    t.string :id, null: false, primary_key: true, default: -> { "'usr_'||nextval('usr_seq_id'::regclass)::TEXT" }
end
```

`connection.execute` is not reversible, so we need to change the method to `up` and `down`. Also, let's update the sequence with an owner, so the sequence will be deleted automatically when the table is dropped.

```ruby
class CreateUsers < ActiveRecord::Migration[7.1]
  def up
    connection.execute('CREATE SEQUENCE usr_seq_id')
    create_table :users do |t|
      ...
    end
    connection.execute('ALTER SEQUENCE usr_seq_id OWNED BY users.id')
  end
  def down
     drop_table :users
   end
end
```

Now, everything is working. I hope you found this article helpful.

### Resources

* [https://www.postgresql.org/docs/current/sql-createsequence.html](https://www.postgresql.org/docs/current/sql-createsequence.html)
    
* [https://stackoverflow.com/questions/17924675/how-to-prefix-a-string-before-sequence-generated-by-postgresql/17925601#17925601](https://stackoverflow.com/questions/17924675/how-to-prefix-a-string-before-sequence-generated-by-postgresql/17925601#17925601)
