Skip to main content

guides/migrating_from_bulk_upsert.md

# Migrating from bulk_upsert

Bulkinup is the continuation of the
[`bulk_upsert`](https://hex.pm/packages/bulk_upsert) package: versions <= 0.5.x were released
as `bulk_upsert`, and 0.6.0 is the first release as `bulkinup`. The upsert behavior is
unchanged — for existing callers the migration is a rename.

## 1. Swap the dependency

In `mix.exs`:

```elixir
# Before
{:bulk_upsert, "~> 0.5.0"}

# After
{:bulkinup, "~> 0.6.0"}
```

Then `mix deps.get`. (All `bulk_upsert` versions are retired on Hex — they still resolve and
compile, but every `deps.get` warns until you switch.)

## 2. Rename the call

`BulkUpsert.bulk_upsert/4` is now `Bulkinup.upsert/4` — same arguments, same options, same
return shape:

```elixir
# Before
BulkUpsert.bulk_upsert(YourProject.Repo, Person, attrs_list, opts)

# After
Bulkinup.upsert(YourProject.Repo, Person, attrs_list, opts)
```

`{:ok, %{upserted: n, skipped: n}}` is unchanged, so `with`/`case` matches on the return keep
working as-is.

## 3. (Optional) Replace a hand-rolled wrapper with `use Bulkinup`

If your repo module wraps the old call — the pattern the bulk_upsert README recommended —
consider `use Bulkinup`, which injects `bulk_insert/3` and `bulk_upsert/3` with app-wide
defaults declared once:

```elixir
defmodule YourProject.Repo do
  use Ecto.Repo,
    otp_app: :your_project,
    adapter: Ecto.Adapters.Postgres

  use Bulkinup,
    upsert: [replace_all_except: [:inserted_at]]
end
```

> #### Delete the wrapper first {: .warning}
>
> `use Bulkinup` injects plain `def`s, so a module that still defines its own `bulk_upsert/3`
> fails to compile. Delete the old wrapper function, then add `use Bulkinup`. Move any
> defaults the wrapper hard-coded into the `use` options (see `Bulkinup.__using__/1`).

## Log metadata

If you filter logs on the library's `:reason` metadata, two atoms were renamed with the
function:

- `:bulk_upsert_changeset_error` is now `:upsert_changeset_error`
- `:bulk_upsert_items_skipped` is now `:upsert_items_skipped`

(`insert/4` uses `:insert_changeset_error` and `:insert_items_skipped`.)

## What's new since 0.5.x

The rename repositions the library around nested bulk *writes*, with two sibling verbs:

- `Bulkinup.insert/4` — pure bulk insert: no conflict defaults anywhere, duplicates raise.
- `use Bulkinup` — repo-scoped calls with compile-time-validated, app-wide defaults.

See the [changelog](changelog.html) for details.