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.