Add query params to Phoenix verified routes with `~p`

almirsarajcic

almirsarajcic

9 hours ago

0 comments

Phoenix verified routes handle query parameters with the same ~p sigil you use for paths. Pass a map or keyword list after ?#{} and get compile-time verification plus automatic URL encoding.

# Pass maps or keyword lists after ?#{}
params = %{page: 1, sort: "name", filter: "active"}
~p"/users?#{params}"
# => "/users?filter=active&page=1&sort=name"

# Inline keyword list works too
~p"/search?#{[q: "elixir phoenix", limit: 10]}"
# => "/search?limit=10&q=elixir+phoenix"

# Single values interpolate directly
page = 3
~p"/posts?page=#{page}"
# => "/posts?page=3"

The params are automatically URL-encoded (spaces become +) and sorted alphabetically. The sorting helps with test assertions - comparing URLs works regardless of param order.

Conditional params

Build dynamic query strings by filtering empty values:

def build_search_url(query, filters) do
  params =
    [q: query, category: filters[:category], sort: filters[:sort]]
    |> Enum.reject(fn {_k, v} -> is_nil(v) or v == "" end)

  ~p"/search?#{params}"
end

build_search_url("elixir", %{category: "books"})
# => "/search?category=books&q=elixir"

build_search_url("elixir", %{})
# => "/search?q=elixir"

LiveView navigation

Query params work seamlessly with push_patch and push_navigate:

def handle_event("filter", %{"status" => status}, socket) do
  params = %{status: status, page: 1}
  {:noreply, push_patch(socket, to: ~p"/orders?#{params}")}
end

def handle_event("search", %{"q" => query}, socket) do
  {:noreply, push_navigate(socket, to: ~p"/results?#{[q: query]}")}
end

Empty params edge case

When params might be empty, use a conditional:

# Avoid ~p"/users?#{[]}" which produces "/users?"
def list_url(filters) when filters == %{}, do: ~p"/users"
def list_url(filters), do: ~p"/users?#{filters}"

# Or inline with if
~p"/users#{if params != %{}, do: "?#{params}", else: ""}"

Links

Comments (0)

Sign in with GitHub to join the discussion