Overview Yesterday, while working on a RESTful , I encountered a somehow tricky problem. API How do I filter an result based on the parameters passed in the URL? Ecto In this mini-tutorial, we’ll build a reusable module that will handle the filtering. Situation We built an API endpoint for getting all the user’s todo items. The table has a column and each todo item can have a state of , or . todos state done doing pending What we are trying to achieve We want to filter the query results based on the request URL’s parameters. For example, we should only include todo items that have the doing or `done` state when the user calls this endpoint: https://my-awesome-api.io/v1/todos?filter[state]=doing,done Boilerplate Usually, to get all the user’s todo items, we have this in our context file: Todos Context Boilerplate And we call this function in our controller like this: Todos Controller Boilerplate Solution Since the filter parameters will be passed in the URL, we need to pass the variable to our context’s function. We need to modify our controller as such: params list_todos/2 By doing that, we also need to adjust the arity of our function in our context. We’re now also going to prepare our context in using the filter module that we will be building later. list_todos : FilterEx is the name of the module that we will building later. NOTE In , we just checked if the filter parameter exists or not. If it exists, we return the in a list form and if it does not, we just return an empty list. line 7 filter parameters We will now build the module which will handle the filtering. FilterEx Building the FilterEx Module In building this module, we will make of use of . If you want to learn more about it, here’s the . Basically, let’s us build query expressions bit by bit and interpolate it later in our main query. Ecto.Query.dynamic/2 official documentation Ecto.Query.dynamic/2 “… it later in our main query” interpolate You may be wondering why we did not just add the call to FilterEx in our context like so: where([t], t.user_id == ^user.id and ^FilterEx.filter(states, :state)) There’s a catch though. can be interpolated at the root of a , or a ’s . dynamic where having join on That’s why we added our FilterEx call after our first clause and not just interpolate it. where It must be interpolated at the root of the clause. Let’s now define the module and import the necessary Ecto module. defmodule FilterEx doimport Ecto.Query ... end expects the first parameter to be the working query, second parameter to be the list of filters and the third parameter to be the column name. FilterEx.filter/3 defmodule FilterEx doimport Ecto.Query @spec filter(Ecto.Query.t, list, atom) :: Ecto.Query.tdef filter(query, [head | tail], fieldname) do...end def filter(query, [], _), do: query end Inside the function, we will be building our initial dynamic query which will be passed to the function for further dynamic query building. Then we interpolate the to our main query and return it to the pipeline. filter/3 filter_field/3 dynamic_query ... def filter(query, [head | tail], fieldname) dodynamic_query =dynamic([q], field(q, ^fieldname) == ^head)|> filter_field(tail, field_name) query |> where(^dynamic\_query) end ... We’ll be using recursion method in our function. filter_field/3 ... def filter_field(dynamic, [head | tail], fieldname) dodynamic([q], field(q, ^fieldname) == ^head or ^dynamic)|> filter_field(tail, fieldname)end ... We have now finished building the FilterEx module. Here’s the completed version: Thank you for reading. If you have any questions, you can always talk to me on Twitter @VinceUrag Learned something valuable? You can always . ❤ buy me a coffee Connect with me on Github: _vinceurag has 11 repositories available. Follow their code on GitHub._github.com vinceurag (Vince Urag)