How to use UUIDs as Primary Key with ecto

Recently, I was playing around with Phoenix Framework (version: 0.13.1) with Postgres as the database. Instead of using IDs for primary key, I wanted to use UUIDs. Since Phoenix and associated projects haven’t hit 1.0 yet, api is still under flux.

As of this post, there are two resources with instructions on how to achieve this and both didn’t seem to work. A stackoverflow post and a google group discussion.

Since ecto version 0.12.1 using UUIDs as primary key coudn’t be any easier. Here is how to do it:

The migration that creates the table will need to be aware of the fact that ID column is going to be of UUID type.

defmodule Blog.Repo.Migrations.CreatePost do
  use Ecto.Migration

  def up do

    # Here we are specifying
    # to not use default auto-incrementing id as primary key
    create table(:posts, primary_key: false) do
      # Here we explicitly set the type of
      # id column as uuid and assign it as primary key
      add :id, :uuid, primary_key: true
      add :title, :string
      add :body, :text

      timestamps
    end

  end

  def down do
    drop table(:posts)
  end

end

We will also need to update our model definition so that it can auto-generate UUIDs for primary keys.

defmodule Blog.Post do
  use Blog.Web, :model

  # :binary_id is managed by drivers/adapters,
  # it will be UUID for mysql, postgres
  # but can be ObjectID if later you decide to use mongo
  @primary_key {:id, :binary_id, autogenerate: true}

  schema "posts" do
    field :title, :string
    field :body, :string

    timestamps
  end

  @required_fields ~w(title body)
  @optional_fields ~w()


  @doc """
  Creates a changeset based on the `model` and `params`.

  If `params` are nil, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end
end

And that is all you need to use UUIDs from primary keys with ecto and phoenix framework.

As an aside, I really like how these projects are shaping up. Also, the community around Elixir and Phoenix is absolutely fantastic. It took me no more than 5 minutes to figure this out by hopping on Elixir IRC and José Valim himself explaining how to solve this problem.

Comments