Guides or advice for migrating to Ash?

rgraff
2023-04-13

rgraff:

I have a small app with 3 tables in Postgres using Ecto.Table. I am migrating them to use Ash.Resource.

When I run mix ash_postgres.generate_migrations for the first time, the generated migrations are to create tables that already exist and the table structures are different. Are there any guides or do you have any advice for reconciling the differences?

Original ecto migration:

defmodule Iterup.Repo.Migrations.CreateAccounts do
  use Ecto.Migration

  def change do
    create table(:accounts, primary_key: false) do
      add :id, :binary_id, primary_key: true
      add :name, :string
      add :slug, :string

      timestamps()
    end

    create unique_index(:accounts, [:slug])
  end
end

New generated migration:

defmodule Iterup.Repo.Migrations.MigrateResources1 do
  @moduledoc """
  Updates resources based on their most recent snapshots.

  This file was autogenerated with `mix ash_postgres.generate_migrations`
  """

  use Ecto.Migration

  def up do
    create table(:accounts, primary_key: false) do
      add :id, :uuid, null: false, default: fragment("uuid_generate_v4()"), primary_key: true
      add :name, :text, null: false
      add :slug, :text, null: false
      add :inserted_at, :utc_datetime_usec, null: false, default: fragment("now()")
      add :updated_at, :utc_datetime_usec, null: false, default: fragment("now()")
    end
  end

  def down do
    drop table(:accounts)
  end
end

Thanks in advance. Excited to move to Ash.

ZachDaniel:

The best way to handle this is generally to generate migrations once, replace the generated migrations with migrations that would change your schema to the new schema, and then move forward with the generated migrations. Generating the migrations once will store the snapshots, and then only new changes will be generated in the future.

ZachDaniel:

So, for example, you might replace the generated migration with something like:

modify table(:accounts) do
  # binary -> uuid should be safe as they are stored as binaries IIRC
  alter :id, :uuid, null: false, default: fragment("uuid_generate_v4()"), primary_key: true
  # string -> text are synonyms IIRC
  alter :name, :text, null: false
  alter :slug, :text, null: false
  alter :inserted_at, :utc_datetime_usec, null: false, default: fragment("now()")
  alter :updated_at, :utc_datetime_usec, null: false, default: fragment("now()")
end

ZachDaniel:

Might take a couple iterations to get right, but AFAIK that is the best way to go about it 🙂

rgraff:

Thanks. I’ll give that a go!