Default (optional) param value in route definition

Hi everyone! I’m trying to find a way to add a default value on a route param in Phoenix. I’ve searched in the forum but couldn’t find an exact match to what I’m seeking.

Example: Let’s suppose there’s a page that shows logs that can be filtered. A user could click an option to filter logs from 7 days up to 30 days and we’d have routes like:

get "/logs", LogController, :index`
  • /logs?days=7
  • /logs?days=30

This is very trivial to achieve because any query parameter is matched against this route, but what if I want to make the days param optional? In other words, filling the value if the user does not specify one.

I know we could simply do that by hand merging the conn.params values with a default map after the action was already matched, but the problem with this approach is that the route won’t change to represent the current page state. If I want to always filter logs that are 1 day old by default, when the user accesses the /logs page, it would have to automatically fill the params as /logs?days=1.

I don’t work with Rails, but it seems that it has similar functionality to other frameworks that I’ve used before: https://guides.rubyonrails.org/routing.html#defining-defaults.
So, I was wondering if Phoenix has something like this built-in or its necessary to create a custom plug.

This can be resolved by simple plug, which redirects to /logs?days=1 if days parameter is empty

1 Like

Don’t match on days, but do something like this in your action:

days = Map.get(params, "days", 1)
1 Like

Are you talking about forward/4? I think it’s only helpful for static routes. For multiple optional parameters, I’d have to match for every possible combination (eg.: /logs?days=7&limit=10&level=error). Also, there’s this disclaimer from the docs:

However, we don’t advise forwarding to another endpoint. The reason is that plugs defined by your app and the forwarded endpoint would be invoked twice, which may lead to errors.

I was specifically looking for a different solution, something more in line with what Ruby does. I guess I’d have to spend a little more time on generic implementation to be able to reuse it with other routes.

Please, see my previous comment on why I’m looking for a different approach than this one:

So you want to have the defaults to be reflected in the URL as well?

Simple…

Use a fallback/catch all clause for your action and redirect with merged defaults.

Wouldn’t this suffer from the same problem as using the forward/4 alternative - regarding processing all the plugs again?

Yes of course.

The last option you have involves JavaScript and push state to use that to add the parameters to the URL on the browsers end, if it does support to do so…

Just using defaults, without having them explicitly in the URL is the easiest solution, does not involve additional round trips or unreliabilities Like Javascript.

Then we have the redirect variants which are reliable, but do have the additional roundtrips.

Last but not least, there is JavaScript and push state, which from the server side just works as already explained for just assuming defaults, but also serve a JavaScript that will normalize the URLs via push state. This will not cause redirects, is reliable to have proper values on the server, but is not reliably showing them on the client.

1 Like