Check out a free preview of the full Build a Fullstack App from Scratch (feat Next.js) course:
The "Protected Route Handler" Lesson is part of the full, Build a Fullstack App from Scratch (feat Next.js) course featured in this preview video. Here's what you'd learn in this lesson:

Scott demonstrates only allowing authorized users, protecting API routes using middleware, and discussing protecting pages utilizing edge functions. Student questions regarding why the user is added for the handler argument and a reason for not using NextAuth for authentication are also covered in this segment.

Get Unlimited Access Now

Transcript from the "Protected Route Handler" Lesson

[00:00:00]
>> All right, so the next thing we'll do then is, we need to start protecting things. So one, we need to create some mechanism to protect some of the API routes that we're gonna make. We're gonna make API route to give the user, an API route to interact with playlist.

[00:00:17] We have to find a way to protect that to make sure that you are who you say you are. You're identified, you're authorized. So we need to make some stuff for that. And then we also need to protect the pages. So the pages as in the index page, eventually the playlist plays, a library page.

[00:00:33] We wanna make sure that you cannot go there unless you're signed in as well. So those are some of the things that we need to protect basically. So we'll do that, the first place we'll do this is probably gonna be on the API site, because we need to make API route to get the user.

[00:00:48] So we can actually go to the homepage and see the user and build out the homepage. But we got to protect that route. And then we got to protect the page. So what we'll do is we'll go back to our files and we will go to pages, api.

[00:01:05] Or actually what we'll do is we'll go to, we'll go to library because we can put all this stuff in the library, and it'll be reusable. So inside a lib, I'm gonna make a new file. I'm going to call it off, that's yes. And here is just going to be like all of our helpers that we use to help us with authorization, authentication, all that good stuff.

[00:01:27] So I'm gonna import jwt from jsonwebtokens or imports Prisma from our personal library. So the first function I'm gonna make is kinda gonna be like a higher order function that we're gonna use this middleware to protect our API routes. So whatever API route that we want to be protected we'll wrap it in this higher order function and we can use this function like a gatekeeper to access an API route.

[00:02:04] So that's what we'll do. So I'll export a new function here, and I'll call it validateRoute. And it's going to be a function that takes in a handler. Remember handler is something that looks like, if you go to pages, api, any one of these functions here's a handler.

[00:02:21] It's a function that takes a request and a response. So we're going to wrap this function. So that means you got to pass that function as an argument. And then what we're going to do is we're going to return our own version of that. So we'll return our own handler that has a request and a response.

[00:02:39] And but not before we check the token. So this function is basically going to check to make sure that you have a token in your cookie. It is a valid token. It's a valid user and if all that is true, then we'll call the handler. If none of that, one of those things aren't true, then we'll 401, like you're not valid.

[00:02:59] And that's how we're protect our routes. So there's many ways to do authentication verification on API routes. And next js, this is the way that I do it, I feel like you're in more control when you can choose what route you want to wrap with this versus just doing it everywhere and trying to opt out.

[00:03:18] So we'll do it this way. First thing is get access to that token via the cookie, so we can just say req.cookies. And if you want to get some autocomplete here, the types for this is going to be I think it's like NextApiRequests and then NextApiResponse if you want those types, so we get the cookie.

[00:03:44] And the name of our cookie is, that we have it here somewhere ,there we go TRAX_ACCESS_TOKEN. So probably want to put this in a constant, or an environment variable or something like that. So we'll get the cookie called TRAX_ACCESS_TOKEN. Or if you call this something else, whatever you call it your cookie.

[00:04:01] So long names, I'm just gonna rename it to token because I don't feel like typing that out many times that's just super long, so I'm gonna rename it there so I restructured it and then I renamed it. That's what this statement is saying, so destructuring it so that's literally like saying const token=req.cookies.TRAX_ACCESS_TOKEN.

[00:04:24] And I would even say this is a better way because they're both on one line. So I, [LAUGH] think the extraction really only matters when you have to do like more than one thing, then it's like, that's cool. But if it's just one thing I mean, I could honestly just say token.

[00:04:42] Equals this is the same thing, so it's just only helpful to do it on the next line or multiple lines of properties. So we got that and then now we're gonna say if you have a token, cool, first check you have a token. Now let's try to get a user from that token so we're gonna do some scoping here because we have to do a try catch because the last thing we want is for our whole API to crash because you sent up a bad token.

[00:05:10] We don't want that so we got to wrap this in a try catch and if you know what? We're gonna do is try to verify this token so we'll say, let's try to get the id back because remember a token when we signed it with an email, an id and a time.

[00:05:26] I'm just interested in id so I want to get the ID back. So I'm going to say given the id if jwt.verify[token] and we got to pass in the same key. The same secret that we use when we signed it was for me, I think it was just hello, you can verify by going back and looking.

[00:05:50] Here, you see that was my secret, was hello or whatever you passed it. So we're gonna get that. We're gonna get the id here, and then I'm gonna attempt to find it using your database with that id. And this will tell me if you are real or not.

[00:06:04] So, prisma.user.findunique where {id} is id. So that'll give me your user or it won't. If it does not, if not a user then I'm just gonna throw an error so I can land on my catch. So I was gonna throw_new_Error, doesn't really matter what you put in here but this is gonna get caught.

[00:06:38] And then if there is an error for whatever reason then I'm just going to say res.status[401], you are not allowed and then res.Json(error) 'Not Authorized'. Or whatever message you want to put, I need to put a return because we're going to continue on and linters getting rid of it when I hit save because I have no code after this try catch And then outside this if statement, Then we wanna return handler, With the request, the response, and then we'll just pass on to user.

[00:07:25] Oops, I'm sorry, not outside this statement, like writing a statement, there we go, outside the try catch. And then outside the if statement, if you don't have a token to begin with. That's when we'll just say, okay. We're just gonna do this, right? We're just gonna for one, Not Authorized.

[00:07:45] If you don't even have a token. Okay, we're not getting into any of this stuff. So again, just to walk through this, this is a higher order function that's going to wrap our handlers, is basically making its own handler. It's checking the token from the cookie that's always sent up.

[00:08:04] Or we're assuming it's sent up where mandating that you send it up and we're going to decode that jwt, turn it back to an object, grab the id property from that. Go to the database, find a user with that id, if all that works out then we'll call the original handler.

[00:08:22] If any one of those things fell at any point as if there is no token, there is no valid user, even if our database broke while we tried to get the user and it wasn't your fault, we're still about a 401, Not Authorized And then the cool thing we're doing is, once we get that user we're passing it along to the next handler so inside of our routes that use this validate route.

[00:08:54] They'll have a third argument after the response. And I'll be the user. So that'd be really cool. So now what we'll do is we'll go to pages, api. We'll make a new Api route for me. Call me.ts and this is just to get the user, it'll just get me.

[00:09:15] So then I'll import the validateRoute middleware we just made and it's gonna be a super short function because the validateRoute function already gets the user. All we have to do is just send it back here, after rewrap it of course. So we'll say export default, validateRoute. We'll pass it our handler.

[00:09:34] And we'll just say req, res, user. And just res.json (user), that's it. So again, this validateRoute wraps our handler. And it won't call this handler unless you're authenticated. And then it gives you the user if it does.
>> Why have the user for the handler argument?
>> Why have the user for the handler argument?

[00:10:01] Great question, so it's just a design choice. If we go back to the auth.ts, the whole point of validating a token, at least the way that I think about it is, going that extra step and making sure that the ID that you got is actually still a valid user.

[00:10:19] You can end up in a scenario where like, yes, this is a valid jwt. Yes, we did sign this jwt. Three days ago but within those three days this user deleted their profile, so they no longer exist in our database. So by doing this extra check of, does this id even exist in our database and getting the user we go extra layer of yes, also this user is valid and since we already got the user.

[00:10:42] Chances are that if there's a route that needs to be protected, it's probably going to use that user data for something, it's probably gonna use that user ID for something like in the case of getting the user object, we needed it. In a case of getting a playlist, we'll need to only get playlists by the user ID.

[00:11:00] In the case of liking a song, we need to get the user ID to make sure that we're saving it to that user's favorite. So chances are if you're protecting a route, you probably need some information about that user. So why not just pass the user alone. Therefore, I don't have to make another query for the user in the database when I get to my route.

[00:11:20] So if I didn't pass this users a third argument, when I went into me, I would have to use Prisma here and query the database to get the user when I literally already just did that inside the validateRoute. So why do it again, and this function never runs unless I'm guaranteed to know that you're validated.

[00:11:38] So it's not like I don't think you're validated. I need to check your for sure validate it. And you already asked for the user, why ask for it again. So that's why I pass it along to save some database queries. Okay, now to protect the pages. It's got to be a little different.

[00:11:55] So many ways you can do this next is it's like, almost overwhelming. There's no wrong way. There's no right way to do this. But they did just come up with a really cool feature, taking advantage of their edge functions. So if you don't know what an edge function is I'm gonna describe it the best way that I can.

[00:12:15] An edge function is basically. So it's you can think of it as one is a service function. So we talked about service functions. All of our API routes are service functions. Instead of a server that's constantly running. Waiting for requests to come into service function is like a call back in that's cloud.

[00:12:33] It only gets executed on an event, which in this case is a route and a file combination. Edge function is, you can think of it as a service function that sits in between your origin server. So in this case, it'll sit in between the service functions that we have our API route, and then our client.

[00:12:53] It'll sit right on the edge of a CDN.
>> Is there a reason for not using next.auth for this project.
>> Yeah, the question was, is there a reason why we're not using next.auth? Yeah, two reasons. One, I think next.auth is an overkill for email password. I would 100% use next.auth if I was doing any social auth, any of that type of jazz, I would totally use it.

[00:13:14] I think I even use it in one of the other next year's courses that are taught. So I really love next.auth. It really got lib I think it's an overkill for this project and adds a little complexity to understand just to get an email password, so that's one reason.

[00:13:28] And then two, those others, yeah, we're not doing social. So [LAUGH], yeah, we're just doing email password. But yeah, next up is really cool. It abstracts a lot of the stuff away when it comes to how often different things like that. But, yeah, back to edge functions. So edge functions are, like I said service functions that sit on the edge of a CDN, and they act as like middleware.

[00:13:54] And this is relatively new technology that a lot of companies have been creating. So CloudFlare workers, lambda edge functions. There's so many different other ones Fly IO is another one. And it's a different environment as well. So it's not quite, Node js. So you can't really do like all this.

[00:14:18] You can't import Node modules and do all this other stuff like that. So it's a little different. About how it works because it has to be fast so it can't spin up a whole runtime. But the really cool thing about it is that you can do computation on a CDN now without having to go to your origin server which just makes your thing so much faster and you can do things like AV testing.

[00:14:39] Preview mode for content, transfer images, you can do all different types of ridiculous things where else before, five years ago, ten years ago you can have computation on our heads. The best thing you can do is set a header and say that, this is how long I want this to catch and then, boom, now you gonna origin.

[00:14:56] So, its actually really powerful, you can build a full app on the edge now, you don't even need a server anymore. You just put everything on an edge function and have a completely functional app like I've literally built Graph QL servers on an edge before never talked to a real server because even databases work on the edge now.

[00:15:14] So depending on what platform you're on