Phoenix 1.7's verified routes

Phoenix 1.7 is dropping path helpers in favor of a new concept called verified routes.

To use a route, we can now use the ~p sigil:

- post_path(@conn, :new)
+ ~p"/posts/new"

That’s all well and good, but you probably have a few questions:

  • How does it handle things like typos and undefined routes?
  • What about paths that have arguments?
  • Does it still implement the Phoenix.Param protocol?
  • How does it handle query params?

Those are all excellent questions! Let’s take a look at them.

The routes are verified

Path helpers were helpful because they were functions.

So, if we used an invalid path helper, we were using a non-existent function, and the compiler would warn us. That protected us from accidental typos and using undefined routes.

Let’s see how verified routes handle those.

Making a typo

Suppose we typo a path, and instead of calling ~p"/posts/new" we type ~p"/post/new".

When we compile our app, we get the following warning:

$ mix compile
Compiling 1 file (.ex)
warning: no route path for ScoutWeb.Router matches "/post/new"
  lib/scout_web/live/post_live/index.html.heex:4: ScoutWeb.PostLive.Index.render/1

Beautiful. The compiler has our back!

Undefined routes

What happens if we use an undefined route?

To test that, suppose we change one of our routes from “posts” to “announcements”:

- live "/posts", PostLive.Index, :index
+ live "/announcements", PostLive.Index, :index

When we compile our app, this is what we see:

$ mix compile
Compiling 8 files (.ex)
warning: no route path for ScoutWeb.Router matches "/posts"
  lib/scout_web/live/post_live/show.html.heex:16: ScoutWeb.PostLive.Show.render/1

warning: no route path for ScoutWeb.Router matches "/posts"
  lib/scout_web/live/post_live/index.html.heex:38: ScoutWeb.PostLive.Index.render/1

warning: no route path for ScoutWeb.Router matches "/posts"
  lib/scout_web/live/post_live/index.html.heex:30: ScoutWeb.PostLive.Index.render/1

The compiler warns us of missing routes and tells us exactly where we’re using the undefined routes! 😍

What about routes with arguments?

Routes with arguments work exactly like string interpolation.

Take the /posts/:id path. You can interpolate the ID there:

~p"/posts/#{post.id}"
# => /posts/23

But path helpers also implemented the Phoenix.Param protocol, which meant we could pass the whole struct as an argument, and the path helper would know to take the id.

post_path(conn, :show, post)
# => /posts/23

Well, rest assured that verified routes do the same!

~p"/posts/#{post}"
# => /posts/23

Love that Phoenix.Param protocol!

What about query params?

My first thought was that we were going to have to interpolate each and every single query param!

q = "hello world"
size = "original"
~p"/posts/#{post}?q=#{q}&size=#{size}"
# => "/posts/23?q=hellow+world&size=original"

That works, but it’s a bit of a pain.

I loved how path helpers allowed us to pass key-value pairs as the query params, and the helper would set those in the query string.

post_path(conn, :show, post, q: "hello world", size: "original")
# => "/posts/23?q=hellow+world&size=original"

Thankfully, the Phoenix team anticipated that and made it so you can pass a map of query params, and the ~p sigil will interpolate all the params and URL escape them!

params = %{q: "hello world", size: "original"}
~p"/posts/#{post}?#{params}"
# => "/posts/23?q=hellow+world&size=original"

Want more?

For more, take a look at the release candidate announcement and the Phoenix 1.7 changelog.

Want my latest thoughts, posts, and projects in your inbox?

    I will never send you spam. Unsubscribe any time.