When working on a JSON-API-like API, the results are often determined by the query parameters. For example, if you have a users
endpoint and you want to include both the users’ profile and company, you might use include=profile,company
.
I found dealing with these types of query parameters to be outside of the scope of Ecto / changesets, and instead created this plug which allows you to define for any query parameter the allowed values, cast and default value. Here’s an example of it’s usage:
defmodule MyApp.UserController do
plug(ValidateQuery,
include: {["company", "profile"], :list},
filter: [name: {:any}, only_active: {:any, :boolean, true}],
)
def index(conn, _params) do
# params["filter"] should have _at least_ %{"only_active" => true} if nothing was given.
# return response
end
end
Here, we define 2 query parameters:
-
include
: A list with either of the two values:"company"
or"profile"
. The:list
atom specifies that the value should be cast to a list. In this case this means that eitherinclude=profile,company
orinclude[]=profile&include[]=company
is accepted. -
filter
: A map with 2 keys,name
andonly_active
, in whichname
may have any value andonly_active
should be a boolean, defaults to true.
An example of a query that is accepted with this definition:
/users?filter[only_active]=false&filter[name]=john&include=profile&filter[age]=30
%Plug.Conn{params: {"include" => ["profile"], "filter" => %{"only_active" => false, "name" => "john"}}}
As you can see, the filter[age]
has been filtered out of the parameters.
Any keys outside of include
and filter
are kept as is (so they won’t be filtered out).