Scoped Finds with Ecto in Phoenix

Last updated 26 October 2016

I’ve written before about setting up a current_user in Phoenix and requiring that user in your controller action. Today I’ll be talking about scoped finds.

What is a scoped find?

In the Rails world, a scoped find looks like this:

def edit
  @post = current_user.posts.find(params[:id])
end

This is a useful security technique that either finds a post that belongs to the currently signed in user or raises an ActiveRecord::RecordNotFound error (which Rails turns into a 404). Doing finds like this prevents unauthorized access without a bunch of boilerplate authorization code.

How can I do a scoped find in Ecto?

Since Elixir isn’t object oriented, you can’t do current_user.posts.find, but you can do something similar. The Phoenix generation tasks use Repo.get! which works similarly to Active Record’s find. For those times when you need to more than an id lookup, Ecto also has Repo.get_by! which is works like get! but with additional parameters.

It could be used liked this for the same functionality as the Rails/Ruby code above:

def edit(conn, %{"id" => id}) do
  post = Repo.get_by!(Post, id: id, user_id: current_user(conn).id)
  render(conn, "edit.html", post: post)
end

This will either find a record or raise an Ecto.NoResultsError (which Phoenix will turn into a 404).

I hope this helps you perform scoped finds when working on your Phoenix apps.