documentation/topics/resources/working-with-existing-databases.md

<!--
SPDX-FileCopyrightText: 2020 Zach Daniel

SPDX-License-Identifier: MIT
-->

# Working With Existing Databases

When you're building an Ash application against a database you don't own or control — such as a shared company database, a legacy system, or a third-party service's database — you need a workflow that lets you iterate on your Ash resources without generating migrations. The `--fragments` and `--no-migrations` options to `mix ash_postgres.gen.resources` are designed for exactly this.

## The Problem

Normally, Ash resources are the source of truth for your database schema, and migrations are generated from them. But when the database is managed externally:

- You don't want Ash generating migrations for a schema you don't control
- The upstream schema may change, and you need to regenerate your resources to match
- You still want to customize your resources with actions, calculations, validations, and other Ash features — without losing those customizations on regeneration

## The Workflow

### 1. Generate resources with `--fragments` and `--no-migrations`

```bash
mix ash_postgres.gen.resources MyApp.ExternalDb \
  --tables users,orders,products \
  --no-migrations \
  --fragments
```

This creates two files per table:

- **The resource file** (e.g., `lib/my_app/external_db/user.ex`) — contains `use Ash.Resource`, the `postgres` block, and any actions. This is *your* file to customize.
- **The fragment file** (e.g., `lib/my_app/external_db/user/model.ex`) — contains the attributes, relationships, and identities introspected from the database. This file is regenerated by the tool.

The resource file will include `migrate? false` in its `postgres` block (from `--no-migrations`), telling Ash not to generate migrations for it:

```elixir
defmodule MyApp.ExternalDb.User do
  use Ash.Resource,
    domain: MyApp.ExternalDb,
    data_layer: AshPostgres.DataLayer,
    fragments: [MyApp.ExternalDb.User.Model]

  postgres do
    table "users"
    repo MyApp.Repo
    migrate? false
  end
end
```

The fragment file contains the schema details:

```elixir
defmodule MyApp.ExternalDb.User.Model do
  use Spark.Dsl.Fragment,
    of: Ash.Resource

  attributes do
    uuid_primary_key :id
    attribute :email, :string, public?: true
    attribute :name, :string, public?: true
    # ...
  end

  relationships do
    has_many :orders, MyApp.ExternalDb.Order
    # ...
  end

  identities do
    identity :unique_email, [:email]
  end
end
```

### 2. Customize your resources

Add actions, calculations, validations, changes, and anything else to the **resource file**. This is your space:

```elixir
defmodule MyApp.ExternalDb.User do
  use Ash.Resource,
    domain: MyApp.ExternalDb,
    data_layer: AshPostgres.DataLayer,
    fragments: [MyApp.ExternalDb.User.Model]

  actions do
    defaults [:read]

    read :by_email do
      argument :email, :string, allow_nil?: false
      filter expr(email == ^arg(:email))
    end
  end

  calculations do
    calculate :display_name, :string, expr(name || email)
  end

  postgres do
    table "users"
    repo MyApp.Repo
    migrate? false
  end
end
```

### 3. Regenerate fragments when the schema changes

When the upstream database schema changes (new columns, new tables, changed relationships), re-run the same command:

```bash
mix ash_postgres.gen.resources MyApp.ExternalDb \
  --tables users,orders,products \
  --no-migrations \
  --fragments
```

Because the resource files already exist, **only the fragment files are regenerated**. Your customizations in the resource files are untouched.

### 4. Review the diff

After regeneration, review the changes with `git diff` to see what changed in the schema. New columns will appear as new attributes, altered relationships will be updated, and so on.

## Key Points

- **`--fragments`** splits generated schema details into a separate `Model` fragment module, keeping your resource file safe from regeneration
- **`--no-migrations`** prevents migration generation and adds `migrate? false` to the `postgres` block
- **Fragment files are disposable** — they are regenerated from the database each time. Don't put custom code in them.
- **Resource files are yours** — once created on the first run, they won't be overwritten by subsequent runs
- You can also use `--skip-tables` to exclude tables, `--tables` to scope to specific schemas (e.g., `accounts.`), and `--extend` to apply extensions to generated resources