Router Essentials

LiveView Routing


[00:00:00] Andrew: Just for your reference. This is actually the. Third time I'm recording this entire thing.

[00:00:05] So feeling great about that.

[00:00:07] Well, either way, here we are. We're talking about Phoenix. We're talking about the router.

[00:00:11] So we're going to break this down into the regular Phoenix side and then the live view side. Cause it's a little bit different.

[00:00:17] Now in order to follow along, you're going to have to open up the documentation. So I've got hex docs that PM slash Phoenix open here. And we're going to be reading the latest version, which is dot 11. But it's going to be pretty similar and other versions of Phoenix. And we're going to be looking at a couple of different pages so you can follow along. We're going to be looking at the guide section. This routing page here. Then there's a couple other guides that are kind of important. There's this request, lifecycle guide.

[00:00:46] Now one thing I need to point out. Is the live view. Documentation is actually in its own hex docs page. So you do need to come here to Phoenix live views, documentation. Once we get to the live view section. And also plug has its own package. So it has its own documentation too. So you will need to reference some of the stuff in here as we go along.

[00:01:09] Let's dig right into it, by going to the guide section and going to the router page. We're basically going to go through this together. And I'm going to do my best to explain some of this. And it's important to know where these pages are in the docs.

[00:01:23] So once this video is done, You know how to continue learning, you know where to go to reference this information in the future. But I'm going to do my best to summarize it for you. So get comfortable. Get a snack, get a drink, whatever you want. And let's learn Phoenix routing together.

[00:01:38] So the first thing I want to draw your attention to is when we're looking at the router, you see how there's all these like calls here, pipeline and plug. That do not have parentheses around them. This is a telltale sign that we are dealing with macros. And if you don't know, the gist of it is that macros are a compile time feature of a number of programming languages. Including elixir. That lets you define code.

[00:02:02] That's going to run strictly at compile time.

[00:02:05] Macros in all programming languages have a reputation for being potentially very confusing. But very powerful. So you can do a lot with them. They can be kind of dangerous. You can write confusing code with them because they can do so much.

[00:02:20] And the docs actually have a little section down here that mentions the fact that the router uses more macros than most of the Phoenix project.

[00:02:30] And it points out that the main reason they are used as for compile time safety and compile time speed, they can optimize all of these matches. To one giant case statement that will be heavily optimized to the whole thing will run a lot faster.

[00:02:46] Example Project

[00:02:46] Andrew: But it can be confusing to know where to look for stuff in here. So the first thing I want to draw your attention to. Is this line here, use hello web. Now we're going to be looking at another project of mine. And this project is an RSS reader. And the specific seven aren't really important, but I have the router for this project open.

[00:03:07] Over here.

[00:03:08] The website is called catnip catnip, RSS. And so the module name is called cat web. So that's what we're going to be looking at today. So I've got my router open for this project. And I have a lot of stuff in here that we may or may not talk about, but it's pretty similar to what comes out of the box in a new project.

[00:03:28] Now this section here, use cat web router.

[00:03:31] The Web Module

[00:03:31] Andrew: This has a module that we can go look at. It is in the web directory. It is at the top level of the live directory. And it's going to be right here. It's going to have your project underscore web. So if we go look at that, there is a router block here and we can make a pretty good guess that this is what is being loaded here. But if you're interested in how this works specifically, I think when people are learning it, isn't immediately obvious to them how this whole file works here.

[00:04:00] Macros in the Router System

[00:04:00] Andrew: But how this works is there's a using macro at the bottom. Of this file here. Which corresponds to these use calls, which is itself a macro.

[00:04:10] And when this gets called. This argument here is going to be passed to which, and that's going to be passed to apply, which is a dynamic function call function. And it's going to look up the function with that name, which in this case is router. So, this is how it results in the calling of this router function here.

[00:04:29] And then all of this stuff is going to get imported. Now it's a little bit more complicated than that in reality, because this is a macro. So it's inside of like a quote block. So the whole thing is going to be like executed as if it was in here. But the specifics of that are not important. This is not a elixir macro tutorial. I just want you to understand where this is. Everything you see in the router here. All these functions, all these macros. Are going to come from one of four places. Either. This use Phoenix router line, which we'll look at it in a second. Or one of these three imports plug conn Phoenix controller. Live iWriter.

[00:05:04] Now, just like this use macro has a corresponding using block at the bottom of the file here. We can also take a look at Phoenix router. And I have a, on my keyboard, G D in VIM map to go to definition. Using the elixir LSP. So if you use VIM, you can configure something similar. Or if you use vs code, I'm sure you have a similar key binding.

[00:05:28] Inspecting the Phoenix Router

[00:05:28] Andrew: But whatever you use to jump into this file, you can take a look at the Phoenix router with me.

[00:05:32] And if we scroll down to this using block, This is exactly what's going to get run when we call you as Phoenix router. Which is basically going to run a whole bunch of imports itself.

[00:05:42] So it's going to run this prelude function, which imports a bunch of stuff.

[00:05:45] This is kind of a common thing. You'll see, on a number of using macros in the Phoenix project,

[00:05:51] where they import inside the using block, the module itself. Interesting that it also imports plug conn and Phoenix controllers since these come from a new project, but I guess that's going to be deprecated soon.

[00:06:06] But yeah, everything in here, one of these three macros here. Is what is going to get imported. When you use Phoenix router is called. So these four things. R where everything in the router comes from.

[00:06:18] Helpers and Verified Routes

[00:06:18] Andrew: Now, one other thing I want to draw your attention to is here in the web module. When we call, use Phoenix router. You may or may not have this helpers false line here.

[00:06:31] This corresponds to an old way of referencing paths. Before Phoenix, This was the way you would go about referencing paths in your app. And I believe this syntax kind of comes from rails rails, had a similar helper method structure for this sort of thing.

[00:06:47] But in, they added a new way of doing this. And so when you set helpers false, It turns off the old way.

[00:06:55] And the new way of doing things. If we go back to the routing guide, It's called verified routes. So instead of having those helper functions, We can actually write these little statements here that you've probably seen in the docs. This is a sigil. This one's called sigil P and then there's a path inside of this string here. And the cool thing about this is because the router is a big compile time plug macro. It actually knows which routes are valid and which ones are not. So if you use the syntax and you reference a route that doesn't exist as they kind of show here, it'll actually tell you that that route doesn't exist. So by setting helpers false and using these instead, you get an extra layer of compile time safety everywhere on the app that you referenced, these paths. Which is not something that I've seen another web framework do, which I think is pretty cool.

[00:07:47] So that's verified routes.

[00:07:49] I just want to touch upon that because it shows up quite early. If you are reading through the router itself.

[00:07:55] Router HTTP Verbs

[00:07:55] Andrew: If we go back to the top of the routing guide, And scroll down. The first thing we really get to is the router definitions themselves. Now they should be pretty familiar to you if you're used to most other web frameworks. They have a HTTP verb, a path, a controller, and an action. This sort of thing is common.

[00:08:18] It should be, you should be pretty used to it.

[00:08:20] And another thing that Phoenix I believe took from rails is this resources macro. If you are using restful routes, which I do not have a whole lot of in the app that we were just looking at, but if you are really into using restful routes, You can use this resources block

[00:08:37] and it will define a whole bunch of routes for you.

[00:08:40] So you see this resource that's called here is going to define all these restful routes users, index users, edit users, new users show. All that with one line. So if you are using restful routes, that may be useful to you. And if we scroll down, it also mentions somewhere in here. Down here. I believe. That you can nest these resources. If you have need of nested restful routes, for some reason. And I will just say that if you want to use restful routes, you know, that you're going to want to do it. So, you know, feel free to use the shortcut if you want, but if you are learning Phoenix and if you find the route or confusing or anything, Just know that you don't have to use this.

[00:09:20] If you don't want to, you can just reference everything directly. As you see here.

[00:09:25] So that's resources. It's pretty easy to understand. It's just a shortening for defining restful routes.

[00:09:31] Plug

[00:09:31] Andrew: Now, if we scroll down. The next thing we're going to come to in the docks is scoped routes. And here is where we need to pause and talk about plug.

[00:09:39] Now plug is an HTTP middleware library. If you come from like rails, a rails has its own middleware called rack. If the concept is totally new to you, it's basically a library that handles the underlying call and response or the request and the response. In rack in rails, it actually does have dedicated request and response objects. But plug works a little bit differently.

[00:10:05] Andrew: It actually has one object called the conn and that conn is shared between the request on the response. So if you're totally new to Phoenix, That's how it works under the hood. It uses plug it's HTTP, middleware to handle routing and a whole bunch of other stuff. And actually the interesting thing is the router itself. Is just one big plug. And we will get to that in a moment.

[00:10:26] Now I mentioned that plug has its own documentation. If you need to reference it, it is here. Just be aware. The Phoenix docs do talk about plug quite a bit, but there is a little bit of extra information there. And there's also this guides page. Now, when we take a look at the definition of plugs,

[00:10:45] There's basically two different ways you can do them. There's these inline function plugs.

[00:10:50] Which are, as the name explains just a function. All plugs take the conn object and a set of options. So that's what they're going to look like.

[00:10:58] But the more common thing. You will see more often are these module plugs.

[00:11:03] These, make it a little bit easier to set the options like this. And they will just be in a module that imports plug conn and they will have a call function. That also takes the con. And it will always do something to the con, whatever that plug may be doing. Now let's look at some of the plugs that come in a new Phoenix project. Everything you see here. Is going to be in a new Phoenix project. Or it's going to be the result of running mixed Phoenix gen auth the authentication system built into Phoenix.

[00:11:32] By looking at these, you can kind of understand what they do and it should be pretty familiar to you.

[00:11:38] They all handle little units of stuff that we need to do on the connection setting, acceptable mime types, checking the CSRF token. They are literally plugable pieces that we can, you know, put in our, out to do stuff to the connection.

[00:11:53] One plug that you will have if you've run the Phoenix gen auth generator is this last one here.

[00:11:59] Fetch current user. Now if we grep for that in our project. We will see that it is here in user auth. And this is a file. That'll come from the generator from the mix Phoenix auth generator. And I think looking at this plug really gives you a good idea of what you will often end up doing with your own plugs. It'll often be stuff like this, getting the current user out of a session token and a cookie or doing stuff like that.

[00:12:25] And then assigning them to the conn.

[00:12:27] So, this is actually a particularly important one . Because this plug is how the current user. Gets assigned across the whole app. So, if we were to look at any controllers that access current user, this is how they're able to access it.

[00:12:39] Pipelines and Scopes

[00:12:39] Andrew: When we look at the router. We see that there are these pipeline definitions. And what they allow us to do is define a common set of plugs that are going to be executed all at once. When we referenced this pipeline later on.

[00:12:53] So for instance, If we go down here to one of our scopes. We can see. An instance of piping through the set of browser plugs.

[00:13:01] Everything in this block. This scope block from site map down to this live view thing, which we'll talk about in a little bit, It's all going to run through this browser pipeline. Everything in this pipeline, each plug is going to run from top to bottom. And they're going to run on the requests.

[00:13:18] These plugs are going to run on the requests.

[00:13:20] Now it's important to note that this pipe through macro can take both a pipeline or plugs directly. So if we look at this example down here in my project, This is a pipe_through where we are passing the name of a pipeline, but also the name of a plug directly. So this is going to run the auth pipeline that I've defined here. Each one of these is going to run. And then redirect.

[00:13:45] If user authenticated is itself, a plug that's going to be added on to the end of this pipeline.

[00:13:51] Authorization and Plugs

[00:13:51] Andrew: And that's an interesting one to look at because it kind of answers the question that I get a lot. Which is how do I go about adding authorization rules to my app? That's what everyone wants to do. They start a new Phoenix project to learn.

[00:14:05] They run the generator. And then they want to figure out how do I make parts of my app for certain users? You know, how do I check the roles of my users? How do I do all that stuff?

[00:14:15] And it's going to get a little bit more complicated when we start to talk about live view. But just talking about plugs. The answer is pretty simple. You're going to do it in the plugs.

[00:14:23] So if you have no live view, That's all you need to think about. You can write plugs like this. Redirect. If user is authenticated is one that comes from the generator. And it checks current user. Checks to see if current user is set. That means that they were logged in successfully. There was a session. In their cookies that the plug higher up was able to get them from.

[00:14:45] And if they are set, this plug is actually going to redirect them. Require authenticated user is probably a little bit more sensible. Because this re redirect of user is authenticated. Is used to protect all these routes that we don't want people to hit. If they are logged in. But there's also routes that we want people to hit only if they're logged in. For instance, if we go down here. This pricing page.

[00:15:08] I only want people to see if they are logged in. So I use require authenticated user. And if we look at that, It's right down here.

[00:15:16] So you can kind of see an example of what you can do in these plugs. We're checking current user. Checking if they're confirmed. So this first block is basically, are they logged in and are they confirmed? If so just return the conn don't do anything. It's fine. You know, accept the connection. But if one of these things are not true. For instance, if they are not confirmed, we can throw up an error and say, you have to confirm your email. Or if they're not logged in or not confirmed, we send them to the login page.

[00:15:43] So that's the kind of things that you can do in your plugs to handle authorization.

[00:15:47] Scopes again

[00:15:47] Andrew: Okay. Now I just kind of glossed over scopes. So I want to come back to talk about that a little bit. If we go to the routing docs and we'd go down to where we were talking about scopes. So we kind of touched on what plugs and pipelines are. These sets of functionality that operate on the con. Scopes interact with those in a way that lets us define a common namespace for our routes and for our pipelines or our plugs. So when we define a scope and we give it this name, That's going to be prefixed on all of the routes inside the scope. So in this example, when we have slash admin, All these here are going to get prefixed with slash admin. And then the other important thing about scopes is they can receive a module name like this. And when we referenced modules inside of here on our routes. That will get prefixed in front of the module. So that's another thing that I think kind of trips people up when they're learning Phoenix, they look at these routes that come from the generator. And they're like, how am I able to call these even though I haven't imported them or aliased them. Well it's because in the scopes. This cat web module is getting imported or everything in there is getting imported, and then they can access those module names on qualified.

[00:17:07] So that's how scopes work.

[00:17:08] Basically. There's nothing magical about them. They're just a way to combine a route prefix and a module prefix, and then they allow you to define some plugs for your routes.

[00:17:20] Now, if you have run the Phoenix gen auth generator, and you're kind of confused how it all fits together. You can check this page and the docs, this mixed Phoenix gen auth page down here. And this does tell you a little bit about behind the scenes, how it all fits together. It's not really router specific, but I think it is important for you to read through because it talks a little bit about some of the plugs that come out of the box.