A second version of this course released using Next 13+, but this course is great if you want to see another example fullstack project. We now recommend you take the Build a Fullstack App with Next.js, v2 course.
Transcript from the "Signin API Route" Lesson
>> So we'll ramp up and pick up where we left off which was I believe I was on step 4, and I pushed up the signup off API function to step 4. So we'll continue with that, and move into making the sign in API function which is pretty similar to sign up, except we're not creating a user, we're just checking to see if the user exists, and then issuing the same token as a cookie.
[00:00:26] So very, very similar. So if you wanna head over to our code as a refresher, all of our API routes are gonna be in the pages slash API folder, and we should already have a sign up TS here. This hello js that got generated, you could totally delete it, we don't need it, it's up to you.
[00:00:46] That's just what got generated with the create next app thing that we did the beginning of the course so we don't really need it. I am gonna make a new file here for sign in, I'm gonna call that signin,ts. And again like I said the strategy for this one is we're gonna expect an email and a password to be sent up to the API, we're gonna check that email to see if there's a user in the database by that email, if there is, then we're gonna compare the hash passwords and see if they are the same.
[00:01:19] If they are, this person is who they say they are, they are a valid user, generate them a token, set it in the cookie, go on with our day. If they are not, we're gonna for a while, like, obviously you gave us the wrong email password combination so you cannot sign in.
[00:01:32] And that's pretty much what we're gonna do. So again, it's very similar to sign up but not quite. So let's go ahead and get started with this, we're gonna import bcrypts and the ccrypt and just like we did in the sign up. Again, if you're using bcrypt JS, it should be the same API.
[00:01:49] So should be able to follow here pretty similar. And then we're gonna import the JSON web token from JSON Web Token or people pronounce it jot, I don't know how JWT sounds out to jot. But, [LAUGH] when you here someone say it jot, that's what they're talking about.
[00:02:08] They're talking about a JSON Web Token. A couple years ago, someone said that to me, I was like, a what? A JWT, and I was like, no, jot, I'm like, okay, I guess that's what we're calling it. So yeah, that's the thing. And then we're gonna import our Prisma clients that we created inside of our lib, so we'll have that.
[00:02:29] And then we're just gonna export a function. So export default async function, actually see export defaults, there we go. We don't really need a name like that, we'll just do error function. There we go and just like every other API, we get requests, we get a response. I'm going to type check them so I'm gonna say Next API requests.
[00:02:58] That way we can get some nice autocomplete and code, code intelligence, whatever you wanna call it. And then the next API response here. Cool. So because I'm making an API, I get to set the rules on what I expect to be posted to this route so because I am making the sign in, I'm saying you must send me an email, and you must send me a password on the body so I'm gonna say given that wrecked up body.
[00:03:31] So we got the email and password here, and then the next thing is let's try to find a user with this email. And if we look at our schema just as a refresher, we know that users have a unique email. So it's safe to look up the user by email because they don't share that.
[00:03:46] So we should be pretty good here. So now I'm gonna do that, I'm gonna say const user equals await prisma.user, get that nice autocomplete dot, and we wanna do find unique, find unique is like fine one if you've ever used any other ORM that's basically what that is, except the only query parameters that you can pass to find unique have to be fields that are also unique on the schema.
[00:04:14] So in this case, for the user, the user only has two unique fields that's in ID and that's email. So we couldn't search for a user using the find unique by I don't know, playlists, for some reason we could not do that. We would have to use a fine mini and do it that way.
[00:04:35] Because it's not indexed and running an index queries can be really, really slow. You just scan the whole database looking for one thing, so you don't want that. That is where like indexing strategies and that's where it gets complicated. So if your full stack are fine and you probably won't have to deal with that, usually some back end person will probably do that but if you're wondering that's where that happens.
[00:04:56] And then we're gonna use the where property here and then we're gonna say where email is email. So find this user where email is email. And then we're gonna do a check. So if we get a user, and we're gonna check that user's password versus the password hash, so we'll say, and bcrypt.compareSync, and the first argument is gonna be the the password that they sent up so the un-hash password and then the next argument is gonna be the hashed password which will be user.password, so look at that If that is the case and we do get that, we're pretty much gonna do the same thing we did in signup.
[00:05:41] We're gonna create a job, set it to the cookie, and then send that cookie back. Well, I guess cookies always sent back we set them on the header and then we're just gonna send the user back to us on a front end. So let's do that, so we'll say const token equals jwt.sign, there we go, so jwt.sign and that takes in of a couple arguments, the first one is gonna be the payload that we wanna create, so in this case it's gonna be an object with an ID, that'll be the user's ID cuz we wanna be able to verify that user.
[00:06:18] Might as well throw the email in there that's unique, and then we'll put a time property on here as date dot now as when we created this. Then the next one is gonna be the secret for your JWT. Again, you probably wanna put this in an environment variable so you can not have it exposed in plain text and reuse it.
[00:06:35] I wanna make sure I'm using the same one, I just used the word hello here, so you have to do the same one. So I'm gonna do that, hello. And then the next one is just some options and I could say, expiresIn, I think we did eight hours to the last one, pretty sure, let me check.
[00:06:53] Yeah, we did eight hours. So I'm gonna do the same thing, keep it consistent. So there we go, we got our token and the next thing is to set that token on the header. So we say res.setheader, and the header that we wanna use is called Set-Cookie like that, so we can set the cookie, and then we're gonna use the cookie.serialize, which is a function that takes in a bunch of stuff.
[00:07:20] So the first thing is we wanna pass in the name of the cookie, and I believe we called it, there we go, TRAX_ACCESS_TOKEN. So like that, in fact, actually I'm just gonna copy this whole config here cuz it's literally the same thing. So we're gonna go back. Here we go.
[00:07:56] So that makes it a little more secure. Max age is how long the cookie is valid for, I made it eight hours the same as the JWT is valid for. Path is like, where or what site like what routes have access to this cookie, just means the whole site.
[00:08:10] Same site, still don't know what that is, need to look that up, but I've always been doing that. If anyone knows what that is let me know. You know what that is?
>> [INAUDIBLE] Do it like when you click on a link and whether or not it should share the cookie to that third party, lax is like the base default and then the other parameter is strict and strict is like it will only go to a first party site, we will not pass the cookie.
>> I think that's funny that it's called lax, [LAUGH] cuz it's like relax, it's just lax.
>> Cuz I was confused as well, I was like what does is stand for? I mean, like, I'm super lax.
>> Yeah, I'm lax, so lax cookie, I like it. Okay, I learned something today.
[00:08:50] And then secure should be encrypted only in production cuz we're not using HTTPS on localhost. So once we do that, then we can just send the user back. So just be rest JSON and then user. So we did all that if these conditions are true, if these conditions are not true, well, we just wanna send back a 401 and then typically I send back an error.
[00:09:17] So I'll say, set status, or I think it's just status, there we go, 401, and then I'll say res.json error, and I'll say like, I don't know, email or password is wrong, something like that. Whatever you want it back, it's your app. And that's it, that's sign in.
[00:09:40] So with this, we should be able to sign in and get a cookie after we've already signed up. We can test this by signing up obviously and then trying to sign in, or we get this used is the credentials of the seated user that we created which was like, here we go, so we can use these credentials to test it out.
[00:10:04] I'm gonna use http post to localhost 3000 slash API slash sign in, and then for my email I'm gonna use the one that we seed it. I think I still have that in my database, I can verify drop my database yesterday or not, but we're about to find out.
[00:10:22] So usertest.com, and then password was just password, so we'll try to sign in here, and there we go. We were able to sign in, we got back password, and you can see the cookie was set here, track access token, so sign in looks to be good. We can test this by just goofing up the password, right click and see what happens.
[00:10:44] So we goof that up, you can see we got email or password is wrong. And then if I goof up the email, We can see we get the same thing, email or password is wrong. Like I said, it's pretty similar to sign up, it's not that difficult as far as a new concepts.
[00:11:01] Literally, the same thing it was just not creating a user.