So what I want to do now is find a way to transfer these values from our client component to the server. For that I'm going to be using server actions. That being said, if you do not prefer them you don't have to use them. If your project uses API routes, this authentication service and everything we learned today will still work just fine. It's just a way to pass something from the client to the server and my choice for that will be server actions which are built-in RPCs in Next.js 14 and I'm just going to show you exactly how simple they are.
So let's go ahead and do the following. I'm going to hold all of my actions inside a new folder in the root of my application. So let's go ahead and create a new folder called actions like this. Now inside I'm going to create an action called login.cs and the first thing we have to do when building any action is mark it as user server. This way our server code will never be bundled with the client code So this is now as equivalent of an API route.
So let's go ahead and export const login. Let's accept the values for now. Let's give them a type of any and let's simply do console log values like this. That's it. This is a server action.
A completely valid server action. What you can do now is go back inside of your components, auth, login form and inside of here what you can do is call the login from actions login. So make sure that you add this import where is it there we go import login from actions login make sure that this is use client like that and simply pass in the values and that's it this is a server action Can you guess where this will be logged? On the server, that's right. So prepare your terminal right here, like that.
And let's go ahead and enter some values. So fake email and one, two, three, four, five, six. And there we go. We have successfully passed our values to the server. Server actions can be as simple as that.
I know there's a lot of examples going on about how complex they are with all the new hooks use form status, use form state. We're not gonna be doing that. That is for progressive enhancement and while that definitely has its pros I feel like that's a whole module that we have to go over before we can even touch out. So I'm not going to be doing progressive enhancement in this tutorial I'm simply going to be using them as an extremely simple way to pass something from the client to the server, right? So that's what we're gonna be doing today.
That being said, if you don't like server actions, you can just as easily done, you know, axios.post your API route and simply pass in the values and then .then and .get. It doesn't really matter but I am going to be using the server actions in this tutorial. Great! So how do we get the pending state from this server action? Well, there are a couple of ways we can do it.
We can manually set pending, you know, let's imagine that we have a useState here and then, you know, .finally here, we would call it and change it to a set pending false but there is an easier way which is by using the built-in use transition from a react so add use transition from react here and let's add it here to the top and we're gonna extract isPending and start the transition from useTransition like this and then what you can do is go inside of your onSubmit function and simply wrap this login inside of a startTransition like this And then what you can do is go ahead and use this is pending to disable all the states you need. For example, I want to disable this input while it's pending. Like this. I also want to disable the password input while it is pending. And I wanna do the same thing for the button.
So disabled is pending. And I believe that now it will only be a very quick second but you should see a blink happen, right? That is the disabled field. So why do it this way? Why use start transition?
Well, you don't have to use start transition but it will be very useful if you ever do any of the next cache or revalidation or redirects here. So if you happen to do revalidate path or revalidate tag, right, which are Next.js cache functions, Usually you couldn't exactly cache the end of them by using .then or something like that. But startTransition can do that. So startTransition can tell you exactly when something like this has ended. So that's why I prefer using it like that and it works just as fine if you don't use those inside.
So this is going to be the way I'm going to be passing things from the client to the server. Again, you're not required to do this if you prefer API routes, sure thing. And what I want to do now is kind of establish how we're going to validate the fields on the server. So you can imagine this code as exactly what you would do in your API route, again, if that's what you're preferring. So first things first, our values are not a type of any.
They are a z.infer and in order to do that we need to import everything as z from Zod and We don't need this and we need a login schema from schemas like that. So Z dot infer Type of login schema like this and there we go now our login form has no errors because this is exactly what our server action is expecting right here and what I want to do now is actually validate these fields because remember client-side validation can always be bypassed it's very easy to do that So what we're gonna do here is add const validatedFields to be loginSchema.saveParse and again validate the values but this time on the back end where no one can manipulate it. And then we're gonna write if validated fields.success, sorry, if not, so make sure you put this exclamation point here. So if we didn't get back a success field from this, in that case, I'm gonna go ahead and return an error invalid fields like this. So if you were doing this in an API route this would probably be something like return response I'm not exactly sure is it a JSON or something like that Basically you would return back with this kind of object.
And let's go ahead and give it a default of success to be email sent. Something like that. So just mocking these things for now. Great, so now we know that our login schema here is validating our fields so let's go ahead and do the following. What I want to do is find a way to use these errors and the success messages and display them on the form and we can do that thanks to these fields which we've set up, form error and form success.
And I'm really not going to complicate this. So I'm just gonna add two state fields, one for error and one for success. So let's go ahead and pass in bounced error, set error, the state from react with the default value of an empty string. So let's go ahead and see where did I import this. There we go, useState and useTransition from react.
I'm going to copy and paste this and the lower one is going to be success and set success. Like this. And then inside of our onSubmit, onThen, I'm going to get the data and I'm simply going to set error to be data.error. And setSuccess is very simply going to be data.success. Like that.
And let's just see if I'm doing anything wrong here. So login, yeah, is also supposed to be an asynchronous function like this. And does that fix it? Data, oh yeah. So sometimes the error can exist and sometimes it doesn't, right?
So here's what I did. So make sure that the login function is an asynchronous function. I mean the server action, make sure this is an asynchronous function. And in here, in order to fix this TypeScript error, we have to be a bit more specific with the type of our error and success which can be a string or undefined. Like that.
And we should no longer be having any TypeScript errors. And every time we hit a new submit let's go ahead and clear all errors and let's clear all success messages like this. Great, and I believe that we should already be seeing something and what we have to do is now just pass the full error message to use error and success message to use success, like that. So let's for now, let's manually just throw an error first because I believe our validation will pass so let's try it with an error first so just return an error object so if I try anything mail and any password there we go I get an error invalid fields perfect And now let's go ahead and enable this back. So make sure you bring this back.
We are only going to throw an error if our back-end is out validation fails. Otherwise we're gonna throw this success message. So let's try that now. And there we go. Now it says, email sent.
Perfect. So this is our first server action. You can see how simple it is to use And you can see how easy it is to control the errors, the success messages and get the pending state using start the transition. And this is completely safe. This is just as you would write your own API thanks to this use server clause.
So this code is never bundled with the client. So you don't have to worry about any secrets being spilled here. Great! And what I want to do now is well create a register form and the reason I want to do it because we can't log in into anything right? There is nothing here I can write anymore except just you know throwing these errors.
So let's go ahead and create our register form. So the first thing we have to do is we have to create this currently 404 route so when you click on don't have an account it should lead you to slash auth slash register so let's go ahead and resolve this so it's not a 404 page so go inside of app auth and you can just copy and paste the login and rename it to register like this and that should resolve this I believe if I refresh there we go now both my login right both auth register and auth login will show this login component but don't worry we're gonna go ahead and resolve that now. So go inside of register, go inside of page. Let's go ahead and rename this to not be login page but instead register page like this. What I want to do now is I want to go inside of my schemas.
So let's go inside of schemas index.cs. Let's copy and paste this schema here and let's create a register schema. So we're gonna have an email, we're gonna have a password, but the minimum value for the password is gonna be six because, well, because this is a register form, so we can tell the new users the new instructions. And instead of password is required here, I'm gonna write minimum, minimum six characters required. And besides email and password, we're also gonna have a name, which is gonna be a string with a minimum value of one and let's give it a message of name is required like this there we go, So make sure you have the email, the password and the name.
And now what I want to do is I want to create a register form. So in order to create a register form we don't have to do much because we have most of our reusable components. So that's going to be inside of components, auth and inside of here. So let's start by copying the login form and pasting it inside inside of the auth folder right and let's simply rename it to register form.csx like that and go inside of the new register form so make sure that you are inside of the register form in your tab here and simply rename this to not be a login form but instead a register form and we are no longer going to be using the login schema so find the import for the login schema and remove it and instead import register schema. Doing it like this will help you see all the errors you have so you know where to change it.
So I know I need to change it here to use the register schema. I know I need to change it here and here in the on submit as well. And I already know that inside of my default values I'm gonna have one more which is a name. So I can simply add a name here as well. Like that.
And let's go ahead and modify this card wrapper. So instead of welcome back it's gonna be create an account. The back button label is going to be already have an account with a question mark and is going to lead it to out login. And I'm going to keep this show social as well because they're going to work equally on login and register components. Great, and there's honestly just one more thing we have to do here.
It's copy an existing form field like an email and paste it. And change this one to have a name of name and the form label of name and the type doesn't matter and the placeholder is going to be John Doe like that and now what we have to do is use this new register form. So go inside of your app folder, go inside of auth register page and remove this import and instead add a register form from components, auth register form and let's use it here. And there we go. Now we have a login form on auth register, we have a back button that leads us to the login form and this one leads us to the register.
And we obviously have to modify a few more stuff. You can see here I have the button login so let's change that. So go inside of the register form inside of components out let's find the login button. There it is and let's change it to create an account or register whatever you want. There we go.
Now when I click here, I should be getting this error. So minimum six characters required email is required and name is required Perfect. So I'm gonna go ahead and just prepare an equivalent server action for this as well So it's not gonna be using the login action instead. It's gonna be using the register action So instead of actions, well, you can just copy and paste this one, right? And let's rename it register.
Like this. Go ahead and rename the export cons to be register as well. And remove the login schema and import the register schema so now you know where to change them and there you go it can stay exactly the same. Now go back inside of your components out register form here and go ahead and find where you import the login action and change it to be the register action from actions register and now you're gonna have this little error in the on submit and there we go the types should be completely fine so if I try this now Antonio test and a password there we go I have a success message that email was sent. Perfect.
So you've just learned the basics of server actions and you just saw how fast we created our register form. So that's how handy it is this little card wrapper which we created so we can easily change the label, the back button and where it leads. And of course well it's easy to copy and paste this form fields as we need them as well. Perfect! So what we're gonna do next is we're actually gonna be using these actions in the register to create a user inside of our database and encrypt their password.
In order to do that of course we're gonna have to connect to a database and we'll start creating some Prisma models. Great, great job!