A brief guide to Ecto.Multi

Svilen Gospodinov
Oct 9, 2017 · 4 min read
Image for post
Image for post

One of the many exciting additions to Ecto 2.0, which was released sometime ago, was Ecto.Multi — a set of utilities aimed at composing and executing atomic operations, usually (but not always, as you’ll see below) performed against the database. Furthermore— it handles rollbacks, provides results on either success or error, flattens-out nested code and saves multiple round trips to the database. Basically you need some Ecto.Multi in your Elixir life.

Surprisingly, many people I’ve spoken to seem to have missed it and have no idea all this functionality exists. If you haven’t used Ecto.Multi — keep reading! If you have, then you might discover a trick or two.

Everything starts with a%Multi{} struct. Regardless of what you’re doing, you always have to provide a new or an existing Multi to most of the functions, which you can easily create by calling new() :

Easy — you just call Repo.transaction(multi) :

Clearly we just ran an empty Multi, which was obviously successful since nothing was performed (and nothing returned in the second element of the {:ok, return} tuple. To make Multis useful, you need to add operations to it.

The most common (and easy) scenario: multiple changesets. Instead of using the usual Repo.insert et al functions, you can use their Multi equivalent. They also accept an %Ecto.Changeset, so it is an easy change to group them into a single database transaction:

The atoms used — :user ,:team and :foo—are chosen by you. You can pass anything (also you can use a string, instead of an atom) as long as it’s unique for the current Multi. The changeset variables are the usual%Ecto.Changeset structs you know and love.

Operations will be run in the order they’re added to the Multi. Often you need the result of a previous operation, which you can get by running a custom Multi operation, like so:

Ecto.Multi.run needs a name for its first parameter, just like Multi insert/delete/update etc, which I have called :user; the second is a function, which provides you with the results of previous operations. The results are just a map, and you can use the unique key to pattern-match and get the result for a specific operation, in this case :team.

Notice that here we call Repo.update — you need to return an {:ok, val} or a {:error, val} tuple from Multi.run. Using Repo.update will give us just that.

Actually, Multi.run could be used for pretty much anything. As long as you return a success/error tuple, it will become part of the same atomic transaction:

Here :pro_users will be available to use for subsequent operations and in the result returned by Repo.transaction. It’s a great way to ensure code is run together with the rest of the database operations. If the :users operation fails or something else, we’ll never get to filter them.

The beauty of Ecto.Multi is that it’s just a data structure, which you can pass around. It is easy to dynamically generate data and combine different multis together, before executing everything as a single transaction:

Using Multi.append/2 we now have a single Multi with all update operations in order. There’s also mergeand prepend.

Once you call Repo.transaction, you can pattern-match the result tuple.

In the case of success, you will receive all {:ok, result} with result being all operations and their successful results.

If it fails, all database operations will be rolled back, and you will be given {:error, failed_operation, failed_value, changes_so_far} which allows to handle errors from specific operations individually and inspect them. Note that changes_so_far simply means “operations that wen’t well until this one failed” and no data is actually left in the database.

This brief guide covers most of the functions available, but as always, refer to the official documentation, which is excellent:

If you find this short guide useful and would like to see more content on Elixir, Ecto and web performance — please share and hit the 👏 button. Thanks!

I’m Svilen — a full-stack web developer and co-founder at Heresy. We’re always looking for engineers who enjoy working with the latest technologies and solving challenging problems. If you’re curious, check out jobs page!

Heresy Dev

Behind the pixels — lessons, thoughts and ideas on software…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store