Now let's go ahead and let's modify our recommend the service so it doesn't include the currently logged in user. So this is one of the users that should not be shown in the recommended tab because that is myself right here. So what I'm going to do is I'm going to go back inside of my recommended service here So make sure you are inside. It's located inside of our lib folder here, and we already have the get self function So this is what I'm going to do I'm gonna define the user ID as default to be undefined and then I'm gonna go ahead and open a try and catch block in the catch I will let the user ID be null like this otherwise I'm gonna attempt to get the user ID by first getting the self from await get self, which we have imported right here and we have created it here. So we use the current user and then we fetch the user into the database here.
And then we're going to assign the user ID to be self.id. The reason I'm putting this inside of a try and catch block is because our Auth service throws errors and breaks functions if it doesn't work. So this way, I mean, you can go ahead and change this if you don't like it. You can put this entire thing in a try catch to encapsulate it, but I wanted to break the app if someone is not authorized. This is my choice of design for this one and because of that I have to do optional stuff like this.
Alright And now what I'm gonna do is I'm gonna dynamically do the query here. So I'm going to define users to be an empty array like this. And then I will assign users like that. So what I have to do is check if I have the user ID and in that case I will do one query. Otherwise I'm gonna do this simple query.
So that's what we're gonna do. So this is going to be for the logged out user in the else here and inside of here we're gonna go ahead and create one for logged in users. So that one is gonna go users, await, db user, find many and let's go ahead and let's give it order by inside of here and I'm going to order them by created at descending as well but I'm also going to go ahead and add a where clause so in here add where and we're gonna write not ID user ID so we exclude the current user So now as you can see since I'm logged in I have no recommended users for me and I also don't have the label recommended because inside of our recommended component if you remember, so inside of the sidebar right here we have the recommended component. If our data is empty we don't even show the label. So that's why this is empty.
So if you want to see the users now what you have to do is sign out of your account like this and then as a logged out user you can see them but if you go ahead and log in it's not gonna work. So this is what I want to do next. I want you to create another user account here. So let's go ahead and fire up all the hooks we need here. So make sure you have npm run dev running and inside of a new terminal go ahead and run that ngrok command with your stable URL or if you don't have a stable URL you can run just ngrok HTTP 3000 like that and that's going to generate a completely random URL but then you also have to change that in the webhook.
So I'm going to go ahead and use this and I will also show you again where you can find this URL. So you have to go inside of ngrok.com, go inside of cloud edge, domains and in here you have your domain. And in here you have a little button, start a tunnel and this will show you the command you can copy and then you can safely paste that right here as I'm doing right now. But remember when you copy it like this it's on the wrong port. So when you copy it make sure you change this number 80 to 3000 So your port is running on the correct version like that.
Perfect. And just go ahead and ensure that you have the matching webhook in Clerk. So inside of your webhooks, inside of Clerk, you should have that domain. Evolved humbly gopher for me. And you can see that that domain matches this evolved humbly gopher right so make sure you have that running and you can go ahead and copy this URL and just confirm in your new tab here.
So I'm going to close the ngrok now. And there we go. You can see that I have the same page right here as it is on a local host. Great. So it means that our webhook is now running.
So make sure you have both of that and I want you to go ahead and create a new account so click login here and go ahead and create a new user which you haven't done before. So I created a new user and I'm gonna give him a name of Antonio and I'm going to click continue and there we go now I'm in this new user here you can see that I'm called Antonio and now in my recommended I have that other user so this time it's not empty even when I'm logged in and if I log out completely I'm gonna see two users inside. So go back inside of that one of the users. It doesn't matter which one because now we're going to implement the follow functionality. Great so make sure you're logged in and what we're gonna build now is this page 404 but the URL is localhost 3000 and slash the name of that user.
So let's go ahead and do that. So I'm going to go inside of my app folder inside of browse right here and what we have to do is create a new folder which is going to be called inside of square brackets username like that and I'm going to explain what the square brackets mean. So go inside of here and create page.tsx and let's go ahead and call this userPage like that and just return a div userPage like that. And when you save, make sure you have a default export and let me zoom in. You should no longer be having an error.
Instead, it should say user page here. And when you click here, you're on the homepage. And when you click here, you're on the user page and you can see how this stays highlighted because we have that little check is active instead of the user item component, I believe. Great, so this is now working and let me just briefly explain what does this username in square brackets do. So this tells the router that this is dynamic part of the URL so it's not hardcoded.
And that means that inside of this page.tsx here we can access that part of the URL. So I'm gonna quickly show you how to do that. So I'm gonna create an interface user page props to include params and those params are going to have a username which is a string like that. So how do I know that username is going to be inside of the params? Well, because that's the name of my variable right here.
So I can name this anything. It doesn't matter, right? This is just part of the URL and how the URL is going to show that inside of our params here. So because it's named username inside of square brackets, this doesn't literally mean username. It means whatever value it's passed in that part of the URL.
And then when you have that, you can assign that user page props here, extract the params and then you can write user params.username like that and there we go. Now I'm on user something else exactly as I wanted it so in here it matches, in here it matches and inside of my URL it matches. Great! Now what we have to do is we have to create the Prisma schema for our followers and following structure and also one thing that I kind of forgot to talk about. So once you create that new user make sure that you have them inside of your database right?
Ensure that this was all correct. So I'm going to open a new terminal and run npx prisma studio here and that should open localhost 555 and there we go I have two users inside of my database so ensure that you have two users as well. If you don't have two users what you can do is remove all the users so just click this and delete all users then go inside of clerk and remove all users from there as well and just start from the beginning. And also don't worry if sometimes you get errors inside of here from webhooks. That is because when clerk webhooks fail they do something called a retry and they're gonna keep retrying until they succeed or until it reaches the maximum amount of retries.
So during development that can happen, right? Because you might forget to turn on your webhook or your local tunnel, right? You might try and create an account and then the webhooks are gonna get out of sync. So it's not a horrible thing to happen but if you keep seeing those errors even though everything is okay in your database that means everything is okay no need to worry. Great so just ensure you have two users inside of your database here and now what I want to do is I want to go back inside of my Prisma schema.prisma right here and let's go ahead and let's create the follow model.
So just below the model user create model follow Let's go ahead and give it an ID which is going to be a type of string and our primary ID with a default value of UUID give it a follower ID which is a string a following ID which is also a string and now let's create relations with the user model. So a follower is going to be a user with a relation with a name of following and fields for that are going to be follower ID so all of this is in one line, right? References are going to go to the ID and on delete we're going to cascade this model so let me zoom out so you can see oh it's a very long line, I don't know if this helps you but this is how it's supposed to be in one line right so follower user relation name following fields follower ID so we work with this field and we reference to the ID of the user right here and if user gets deleted we will also delete this relation with them right so let me zoom back in and one more thing that you need to do here is go inside of the user model and now you have to create an equivalent relation back.
So after bio go ahead and add the following so you're going to add following like this and then you're going to say follow at relation following like that so follow the follow model which can be an array and array relation of following and there we go now we no longer have an error here but what we do have is a warning so we have to create an index so at that index here go ahead and write follower id and there we go the error is gone and now we have to create an equivalent relation for following right so following is also a user with a relation which we are going to call followed by fields are going to be the following id so that was the follower before, but now it's following ID. References are going to go to the ID and on delete is going to go to cascade. So I will zoom out again so you can see. It's very similar, right? So this is for the follower and or the ones that this model is following and this is for the ones that this model is followed by.
They both, this one uses the follower ID and this one uses the following ID. They both reference to the ID and they will both get deleted if the main user that this refers to is deleted. So just as we created a back relation for the following we now have to create a back relation for the followed by. So let me go back in here and write followed by I believe or yes like that and let's give it again a follow model but the relation is going to be followed by like that and now we no longer have an error here but we do have we do have a warning so let's go ahead and add an index to be following ID. Is it following ID?
And there we go, no more errors. And Besides this, let's also go ahead and let's add at at unique. So we create a unique combination of follower ID and following ID. And that's also going to create an index for us for faster querying. And besides this, let's also create a created at, which is date, time, default now.
And let's add updated at, which is date, time, updated at, like that. Great, and now that you added this model here, what you have to do is you have to run your Prisma commands, right? So make sure you don't close any of your terminals here. Open a new one and let's go ahead and write npx prisma generate. So this will add it to our local environment and then npx prisma db push like that, that's going to push it to the main database in my case on planet scale.
And there we go. And after you do this go ahead and restart your app. So find where you do npm run dev and do npm run dev again so you have new models inside otherwise you're going to get errors during development. So once you've done that just refresh your page like this so everything is up to date. Great!
And now what we're gonna go ahead and do is we're going to create a follow service right so let's go and close everything here let's go inside of lib and let's create a new file follow service.ts like this, great. So let's go ahead first and let's check if we are following a user. So I think that would be a useful util for us. So let's import database and let's go ahead and let's import getself. So import database and getself and I'm just going to change those two to use lib and then let's export const isFollowingUser is going to be an asynchronous function which accepts the ID of the user we are trying to follow And then let's open a try and catch block.
And in the catch block, we're just gonna return false. So if anything goes wrong, we're gonna fall back to telling them that they are not following this user. So now let's go ahead and attempt to get self. And then let's get the other user using await db user find unique where we have a matching ID of users. If we don't have the other user, in that case, let's throw new error, user not found.
And that's by itself going to return false here in the catch block. And now let's go ahead and check if otherUser.id equals self.id. So if the user we are trying to check whether we are following equals ourselves no need to even search through the database we're just going to fall back to true so we are always going to be a follower of ourselves right and this way we're not going to be able to follow ourselves either and now let's go ahead and do const existing follow to be await database.follow which we now have .find first where follower ID is self.id and following ID is otherUser.id and then we're just gonna return whatever is the result of that and turn it into a Boolean by adding double exclamation points existing follow like this. So we find the existing follow in the follow model by passing the follower ID to be the person that is checking whether they are following and the following ID to be that user that they're trying to check their relation with. Great, so now that we have this is following user, it's gonna be useful for us to create another service called user service.
So let's go ahead inside of the lib folder and create user service. And in here let's do export const get user by ID sorry get user by username let's do that one first So because that's how our URLs are created, right? So getUserByUsername is going to be an asynchronous function which accepts the username, which is a string. And we're going to attempt to get the user. So wait, db, which you can import from .slash db.user.findUnique where the username matches.
So as simple as that and just return the user and I'm going to change this to be add slash libdb. So now we have get user by username and what we can do now is go inside of our app folder browse username page dot vsx here and now that we have this username params let's turn this into an asynchronous function here and let's go ahead and write const user to be await get user by username and pass in params dot username like that and if we don't have a user we're going to throw not found from next navigation. So that's a cool little thing that you can do. You don't even have to return this because this will return by itself, right? So make sure you import not found from next navigation.
And then you can use this user to show the username. So user.username and let me do it like this. I'm going to create a div with a class name flex, flex call gap y4. So just some temporary styling here and let's turn this into a paragraph. And let's go ahead and give it something else.
So this is gonna be the username. And then this is gonna be the user ID. And that's gonna go to user.id. Like that. And now, when you look at my page here, for user something else, the username is something else and I have the user ID, so this is directly from my database.
But if I try and change my URL to something that doesn't exist, I have a 404 page exactly as we want it. So go ahead and click on this page here and let's continue to work here. And now what we can do is we can also check whether we are following the user or not. So after this has been confirmed that this user can be fetched from the database, let's add const isFollowing, we await isFollowing user and you can import isFollowingUser from s slash lib follow service and go ahead and pass in user.id like that and then below that we're going to add isFollowing isFollowing like this. And let's go ahead and just see why this is not working.
So isFollowing is not rendering anything for me. Let's go inside of the isFollowingUser here and check why that is happening. It should return true or false. Maybe it's because it's not serialized. So if I turn this into a string will it then change?
No. Oh, yeah, it looks like it's because of string thing so render it like this open backticks and then render it like that there we go so now it's working so we have the username the id and is following is a false so now what we're gonna do is we're gonna create an action to actually follow the user. So we have to go back inside of our follow service and after isFollowingUser, go ahead and create followUser action, which is an asynchronous function which accepts the ID of that user right and let's go ahead and attempt to get self. So I wait to get self and I'll need to put this in a try and catch block like we did with this one because this one is important to be inside of try catch because logged out users can run this function and this function is always going to break for logged out users. So it will just fall back to telling them that they are not following anyone, right?
But for an action to follow user should not even be initiated by someone who's logged out. So we don't care if any error gets in the way, we need to break the function, right? Everything here must work. So now let's attempt to get the other user using await dbuser find unique, where we have a matching ID. If there is no other user, go ahead and throw new error.
We're going to say user not found. Now let's see if the user that this user is trying to follow is themselves. So if other user ID is equal to self ID, we're going to throw a new error, cannot follow yourself. All right, and now let's check if this user is already following another user. So const existing follow is await DB follow find unique where follower ID is self dot ID and following ID is other user dot user.id and instead of find unique let's do find first like this, so this function will work And if we have an existing follow, let's throw new error, already following.
All right, and if all of those tests have passed, let's do const follow to be await, to be await db follow create data follower ID is self ID and following ID is other user dot ID and let's also include following true and follower true. So we're gonna have access to who was followed and by whom in our result from this function. And let's return follow. Great, so now we have our function to follow the user so what I want to do now is I want to do a little practice with server actions here I have a more in-depth tutorial about server actions in my Trello clone if you are interested in that. And the next JS documentation has great explanation and even a little course on server actions.
So you can take that if you're very interested in them. But we're going to do the most basic version of them here. So inside of your username in here let's create underscore components and let's go ahead and create a new file actions.tsx like that. Let's mark them as use client and let's export const actions here and let's return a div or we can actually return a button component. So import that and let's write follow and let's give it a variant of primary.
All right, and now go back to the page and below this add the actions from .slash components actions which we just created. Great and now you can see how I have a huge button here which says follow for that user. So let's go ahead and do a little crash course on actions here. So actions are built in RPCs, which can replace, they allow us to do API less mutations. So the same way that server components allow us to do API-less querying, server actions allow us to do the equivalent but for mutations.
So we don't even need to create a post route for this which is something you might have been expecting with server actions you don't need to do that so this is built in RPC so let me show you how you how we're gonna do that I'm gonna close everything and I'm just go ahead and click on some random item here so you can create a new folder called actions like this and then inside create a new file follow.ts like that and what you're gonna do here is you're gonna first mark this as use server like that. So it's now not gonna be, well, first it's going to work. And second, this is what ensures the security so that this actually behaves like an API route and doesn't accidentally spill into some JavaScript bundle, right? So it's protected. And now let's do export const on follow to be an asynchronous function, which accepts the ID.
And let's go ahead and open a try and catch block and inside this catch block I'm actually going to throw new error internal error here so just like I would on my API route and then inside of try here what we're gonna do for now is just console.log I am the same as an API call right? Obviously it's not the same but just to help you understand and you can also pass in the ID from here, right? And now let me show you how you can call this action from a client component. You can also do it from server components but I'm gonna focus on client components for this case. So go into Browse, Username, Components, Actions in here.
And now what you want to do is you want to import that on follow from your actions here and then let's do const on click here to call on follow and let's pass in one two three like that and let's give it an on click here on click and go ahead and open your terminal and prepare this so where you're running npm run dev and now if I'm not mistaken when you click follow here there we go you can see that it's logged in my server so just like this just like this would have been an API call this is logged in the server from our client component so that's RPC right those are server actions and now you might be wondering can you do pending and loading states with that yes you do and react actually has built-in hooks for this yeah an important thing for you to understand, so server components and like this server actions they're not necessarily a Next.js thing. They are a React thing. It's just that they perform very well inside of Next.js because Next.js seems to be built around server components, right? So what you can do here is import useTransition from React like this.
So in here let's go ahead and extract isPending and startTransition and then wrap this inside of start transition like that and if you're wondering what does this do now well now you can use this is pending on disable right And now if you try and click follow, you can see how for a second the button gets disabled, right? Of course, it's very fast because all we do is throw back a console log, but you can see that this startTransition allows us to get a proper is pending without us manually doing you know set is loading the true and then do a dot finally or whatever else we do to get that. So we can do that quite easily using use transition and server actions. Great So what I want to do now is just a couple of more things. So go back inside of your page.vsx here and let's use this isFollowing and let's pass it as a prop here.
So isFollowing to be isFollowing like that go inside of actions here create an interface actions props isFollowing is gonna be a boolean and go ahead and give them here so actions props is following like that. And then go ahead and let's, I'm just gonna collapse this three attributes for this button here. So give it a disabled if is already following the user or if it's pending. Like that. Great, so by default, this should be turned on, but what you can do is you can try and go to your username.
So copy your logged in username and change it inside of your URL. And now, as you can see, it's disabled. Why? And you can see that it says is following true because that is ourselves right and inside of our follow service inside of the lib here we have follow service here you can see that if it's ourselves, not follow user, sorry, this one is following user. So if it's ourselves, we always return true.
So that's why this exists, even though in our database, we don't actually have a record that we're following this user but I think that's a common practice to do to just disable the user from trying to follow themselves. So now go back to this other user where you should be able to click on this follow button here. Great And now what I want to do is go inside of these actions here and I want to create a little success and error messages so we can go ahead and play around with that. So go inside of your terminal here and I'm going to go ahead and open a new terminal and add npm install Sonar like this. This is going to be a package we're going to use to trigger toasts.
So now go inside of your app folder inside of your layout here and you can just go ahead and import toaster from Sonar like this and then just go ahead and wrap your and add a toaster somewhere like this so I'm gonna add it inside of the body here actually we can do it inside of theme provider maybe just above the children and one more thing that you can also do here is give it a theme and a position so I actually gonna keep my toasts in a light mode because they just look better and they're more noticeable and they're very small, right? So just like our hints, you can see that our hints are in light mode, right? They're not dark because they're more visible this way. And it's the same thing for toasts. So I'm gonna give them a light theme and I'm gonna give them a position of bottom center like this because that's where we're going to have the most space right here in the middle and on the bottom.
Great, so just add that toaster from the Sonar package which we just installed and now let's go back inside of browse username components actions and you can just as easily as you would in an API request you can just as easily chain .then and .catch for server actions so go ahead and import toast from sonar so I'm going to move it here to the top and add toast.success followed the user right or .catch toast.error something went wrong like that and let's go ahead and try this out now so when I click follow there we go it says followed the user right perfect and we can try an error as well by going inside of the actions follow here and let's throw new error something and now if you try you can see that we have an error. Great! So both of our use cases are working. So now what I want to do is also add one more prop to this actions and that's going to be the user ID. So it's a string and user ID here.
Now go inside of your page for the username where we render all of this stuff and pass in the user ID to be the current user.id. Like that. So now our actions know what to send inside of this onFollow() function right so let's go ahead and send that here onFollow() and then let's go ahead and actually develop this follow() function so instead of this const log what we're gonna do is do const followedUser to be await followUser from our lib followService like this and pass in the id of that user. And then, well, let's first, actually, let's immediately do this. So what we can do from Server Actions, this is a cool thing, we can revalidate our paths, meaning that we can kind of refresh server components so that they instantly have the newest data.
Because think of how you would usually have to do this. So in here we just loaded this isFollowing state which says false and then inside of the actions we follow the user. And usually you would have to update your global Redux store for this exact page and change optimistically, right, a mutation from isFollowing false to true. But with ServerActions and ServerComponents you can just revalidate the path and then it's going to be quite easier. So let's go ahead and do this.
Import, revalidate path from next cache and then let's go ahead and do the following. First path I want to re-invalidate is the global root path like that. So my sidebar gets updated because later we're going to have alongside list of recommended users, we're going to have a list of followed users. So this revalidate path will refresh that and recache that, right? And then if we have the followed user from this function because remember inside of follow user here what we do is we return this new follow model which we created and we also include the users which were followed and which followed that user.
So we're going to have information like username ID and that's going to be useful for us because we need to revalidate the path that goes slash and then the name of the user, right? So it should be slash Antonio or slash something else in my case. So go ahead and let's do that dynamically by using the followed user dot following dot username. Like that. Great.
And in the end the return followed user great, so now we have this function let's go inside of actions here and what I want to do next is I want to add a data here access to the data as you can see it right here and let's go ahead and let's dynamically write this so I'm gonna say upload the user or let's say you are now following and then write data.following.username like that, I believe it's .following, let me just check yes, it's .following perfect, so I think that we're ready to try this out I'm gonna prepare my Prisma Studio, so npx Prisma Studio, like this and let me just open that on localhost 5555 like this. You can see that in my follow model I don't have absolutely anything inside. So what should happen is when I click on this follow, I should create that model. So let's click follow, it's disabled, meaning it's loading. And it says you are now following someone else.
And you can see how it immediately revalidated this path so this changed from is following false to is following true and now this button is entirely disabled right and let's finally check my Prisma Studio and in here there we go We have a new follow model with the correct follower ID and correct following ID. Perfect! So our follow system works great. So what I want to create now is an option to unfollow the user and just for fun let's try and not disable this if we are following the user so what we should have what should happen now is an error so when I click follow there we go something went wrong exactly as we expected because we are already following this user perfect So what we have to create now is a function to unfollow the user. So we have to go back inside of our follow service.
So let's go inside of lib. Follow service right here and it's going to be very similar to this function followUser() so let's write export const unfollowUser() is again going to accept the id which is a string and it's also going to be an asynchronous function Let's go ahead and get self from a way to get self, which we already have imported here. Let's get the other user. So wait, leave the user find unique where ID matches, right? We're gonna check if the user we are trying to unfollow exists.
So if the other user does not exist, we throw a new error. User not found. Now, let's check if we are trying to do some action on our own user. So if other user and self has the exact same ID, we're going to throw a new error here and say cannot unfollow yourself. To be honest, we can't even follow ourselves, but that's something only we know, right?
Basically, we don't want to create or delete any models regarding following for ourselves it's just gonna complicate things and not much is gonna be achieved here And now let's check if the user is actually following this user that they are trying to unfollow. So const existingFollow is await db follow findFirst where follower ID is self.id and following ID is other user.id and now if there is no existing follow in that case we're gonna throw new error which says not following So we are trying to unfollow someone who we are not even following. So there is nothing for us to do here. We cannot delete any record. Otherwise, if these tests have passed, So just make sure that you put a little exclamation point here at the end, right?
And let's do const follow to be await db.follow.delete where id is existingFollow.id and include following to be true. And return the follow itself, like that. So very similar function as our follow user but in here we do the opposite. We get the existing follow and we check if it does exist so right here on the bottom we check if it doesn't exist but in here we check if it does exist and then we throw one error and then we create a new one because it should not have been existing and in this one it's the opposite right so in here we delete the follow model. Great so now what I want to do is I want to go inside of actions here inside of follow and I want to go ahead and create an on unfollow function.
So let's go ahead and export const on unfollow to be asynchronous, accept the id which is a string, open a try and catch block and let's throw new error, internal error. And now let's do a const unfollowed user to be await unfollow user, which you can import from the follow service. So we have both the follow user and the unfollow user here. And passing the ID and then let's do revalidate path. And if we do have unfollowed user information, In that case, let's also re-invalidate and let me just not misspell this.
So unfollowed user and let's also revalidate slash unfollowed user dot following dot username. And let's see if it looks like there is some slight mistake here. Let's also return unfollowed user. So I think I forgot to include this. So go back inside of unfollow user here.
And in here we do add include following true so why exactly is it not working here following does not exist on this type so unfollow user But it looks like we do include this so I'm not exactly sure why. Let me go ahead and quickly debug this. So I'm still not sure why this is happening. I'm gonna try... So I pressed command shift P and I'm gonna try and reload my window first to see...
Okay, it's still not that, so it's not some cache. Perhaps it is... What if I add follower true here? So this is my on unfollow user right here. Do I have access to that?
Follower. I don't have access to that either. So why is it like that? So unfollow user it is an asynchronous function We await for this We also await for the delete and we also return the follow where we... I'm really not sure, so for now let's just let's just go ahead and let's do this.
Following username like that. Following does not exist. Untype id string. It could be that it's some very simple mistake that I've made but for some reason I cannot figure out what it is. And the bug is very very obvious and I just didn't notice it.
So this is how I debugged it. So I kept looking at this property here and it says property following does not exist on this type and it's a function which returns a promise So that's obviously not what this returns And then I noticed that in here I wrote unfollowed user and in here I wrote unfollow user which is our function. So that's why it's not working. So this would definitely not work. We have to use this unfollowed user and there we go now we have no errors right here and also make sure you return unfollowed user so maybe a better thing would be user we unfollowed so it's pretty clear right So just make sure you don't accidentally reuse this follow user.
It could have happened in here as well. You can see it's very close. Followed user and follow user. One is a function and one is a result from that function. So make sure you don't do the same mistake.
So we don't have to include follower here inside of our inside of our unfollow user function. Great, so this is good. This is good as well. Great, So now what I want to do is I want to use this on unfollow so let's go ahead back inside of our app folder browse components whoops, username components actions here and this is what I'm going to do I'm actually not going to disable it if we are following instead I'm just going to change the text So if we are following the user the action is gonna be unfollow otherwise is gonna be follow like this. So now for this one it says unfollow because is following is true in the database and then what I'm gonna do is I'm gonna change this to be on follow actually handle follow right and I'm gonna copy and paste this And this one is gonna be handleUnfollow.
And inside it's not gonna use onFollow, it's gonna use a server action onUnfollow from actions.follow. So make sure you import onFollow and onUnfollow which is the one we just had this annoying little bug here when I forgot to add the proper variable name. Great, so now we have handleFollow and handleUnfollow and now I'm gonna change our const on click here to be if is following handle unfollow else handle follow like that. All right and let's just modify the handle unfollow to say you are, you have unfollowed the user like this. And I believe this should already be working.
So right now in my database, I have one follow model here inside of my user here I believe I have a proper relation here there we go so you can see that this new user is following this user because it has a followed by which is this user right So let's check if all of this will be deleted once I click unfollow. It's loading and it says you have unfollowed this user. You can see how this says is following false. Let's check our database. So my database for follow is empty and let's check my user here.
They should and they don't have any relations between them. Perfect! And if I try and follow them again it should be working. There we go. You are now following someone else and in the Prisma Studio they should now have relations.
There we go, they are following one another. Perfect! So you just finished the follow system and what we have to do next is we have to create an equivalent sidebar place to show our followed users the same way we show our recommended users. Great, great job and I hope this kind of cleared up how you can work with server actions, right? They're actually very simple, they're not complicated at all, they're even simpler than your usual API routes, right?
And again, sorry for me not seeing this little bug here. It was very obvious, but you know, too many words at once. Great, great job.