Simple configuration for UUID primary keys in Ecto

    Using UUID’s as primary keys can have many benefits, and if you’re using PostgreSQL you have the advantage of solid performance and efficient storage of those UUIDs leading to good scalability even compared to bigint.

    With Ecto, using UUID primary keys is very easy, however most guides out there are still written for Ecto 1.x or 2.x – in modern times using UUIDs is even easier with Ecto 3+.

    In your config.exs, simply configure your repo to use the :binary_id by default for primary keys and foreign keys:

    config :my_app, MyApp.Repo,
      migration_primary_key: [name: :id, type: :binary_id],
      migration_foreign_key: [column: :id, type: :binary_id]
    

    You don’t have to modify your migrations at all; Ecto will use UUIDs for keys unless you tell it otherwise, just as it was previously with bigint keys.

    The only other required step is to annotate your schemas so they use :binary_id as well. You can do this on each individually, but I generally prefer to do this in a behaviour that wraps Ecto.Schema. For example,

    defmodule MyApp.Schema do
      @moduledoc """
      Behaviour to replace `Ecto.Schema` to set some defaults across all schemas in
      the codebase -- namely, UUID primary keys.
      """
    
      defmacro __using__(_opts) do
        quote do
          use Ecto.Schema
    
          @primary_key {:id, :binary_id, autogenerate: true}
          @foreign_key_type :binary_id
        end
      end
    end
    

    Then you can use this in your schemas:

    defmodule MyApp.User do
      use MyApp.Schema
    
      ...
    end
    

    And that’s all there is to it! I hope this helped you avoid setting primary_key: false in every migration you write.