Build and Deploy a Full Stack Realtime Chat Messaging App with NextJS 13

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I did it again eight hours or nine Jesus Christ hey what's up in this video You're Gonna Learn some of the coolest Tech in the game for example the website chat GPT that has been the fastest website to ever reach a hundred million users is using the same technology as we are in this video that being next.js and also next auth for authentication however the application is gonna differ quite a bit in this video you'll learn how to build a super fast scalable real-time messenger app using redis as the in-memory database making or messages super fast and also making the authentication super straightforward to build this app we're going to use the latest Nexia s13 tag there is being like the app directory the new loading State handlers the new API raw tenders and everything of the good stuff that Nexus s13 offers to us this video is kindly sponsored by upstash which has really allowed me to go into the details and make sure this video is as beginner friendly as I could possibly make it all the concepts that I'm going to introduce to you to build this application I'm gonna go through with you you're gonna learn how middleware works not just how to write it but also how it works in fact I won't even assume that you know next JS I'm just going to assume you know a little bit of react and I'm trying to build your first next year s project at all and we'll also be using a super in demand language that being typescript instead of JavaScript for this I'm not even going to assume you know typescript all the stuff I'm going to introduce to you step by step in this video we're gonna go through in a very logical order only introducing dependencies as needed things you're gonna take away from this video and that you can then apply to your own projects are gonna be first a fully responsive design this design is super well optimized for mobile beautifully animated secondly authentication just like the big boys like Chachi PT do it I'm using next auth super good authentication then very importantly you're gonna learn how to build super fast and scalable real-time Time Communication features to do that we're going to be using a websockets and then secondly redis as our database for super fast database communication one more thing you're going to learn is how to properly protect your routes not anyone should be able to read your chat security wise that will be horrible and we're gonna learn how to prevent that so you're going to learn middleware to prevent unauthorized access to any route that you want in our case that being the chat and also you'll need to be authenticated in order to use this app and even though that's not all you're going to learn you're going to learn way more cool stuff than that that you can take away for your own projects just for a quick summary you're going to learn nexjs13 a super in demand framework even officially recommended to write react code in by now secondly typescript vary in demand language especially since a few years and then in the single video you'll not only build this app but we're also going to deploy it to the web together now along the way you will also learn industry standard best practices how to leverage server components and simultaneous database calls for maximum performance and so much more I'm really excited to do this with you let's get into the application let me give you a demo of what we're going to build and then we're gonna go through step by step in a very logical order starting with the very foundation for example installing Tailwind creating reusable components like a button then going on to the database authentication implementing the adding of friends then working on the actual actual chat functionality and then implementing real-time features and deploying the app in the end and without further Ado I could go on forever what you're going to learn in this video you'll see that along the way let me give you a demo of what we're gonna build and then slowly get started with the actual build so if we navigate to our page this is where we land as I said earlier this is going to be an auth only page meaning only authenticated users will have access to this application we can go ahead and log in with our Google credentials for example this account right here and if I can click that we land on a beautiful minimalistic dashboard with our most recent chats if we had any and then secondly we can see right here on the left hand side there's a slide over where we can add friends and we can also see our incoming chatric or friend requests with or information we can use to add new friends down here where it says or name enter email and then option to logout now if I navigate over to a second client and log in with my support at wordful.ai M email let me quickly type in my password over here and then let's put this into a split screen view so you can get a really good feeling of what's going on in this app so I'm gonna go click on add friend over here on the left client and add or other account right here on the right side as a friend by clicking on this add button right here and when I do that pay attention to what happens right here on the right hand side on the other client under the friends requests I'm going to hit add and we can see that was a very fast and then B we can see a friend request send success message on the left side and we can see in real time that the friend request has been received on the right hand side client and if I now accept this there's a bunch of stuff that's going to happen first off both chat partners are going to appear as chat partners for each other check this out if I press the accept button the front request is going to disappear and we're going to have the wordful AI support as our chat partner in the left hand side and the other account on the right hand side and now if I navigate into the actual chat and type a message hello dude for example and if I zoom in this might be a bit easier to see and hit post then we can see there is a beautiful toast notification popping up on the other client notifying this person that they have received a new message and the way the the reason I'm scrolling out so much is so we don't get into the mobile view which is beautifully responsive by the way as you can see right here it's a beautiful slide over that we can use and we can see there's a new message that just came in from this user that we can answer hello how are you head post and as you can see this is well fast right these messages are instant it's like uh you know it's just a beautifully done real-time communication between these two clients right and we can add more friends we can see our friend requests we can log out and land but right back on the login page and if I try to navigate to this path without being logged in see what happens nothing happens we can't access the chat it prompts us to log in right away because the routes are protected great and that's the demonstration I wanted to give you it's a very cool app you're going to learn a lot on the way and before we start writing code let's get you up to speed on two core principles that we're going to use in this application first one being a different routing system than you than what you might be used to from react so in react the way we handle grouting for example with react router would be we Define a path like the dashboard path and then a component that should correspond to that path that's how we did it in react in next.js the routing structure is fundamentally different because we are going to be using a file based routing system and what that means is we create folders that represent the URL path and then inside the folder we're creating the pages that then represent the routes so let's say we wanted to recreate the same thing in next.js as with the react one we want to have a slash dashboard and then a dashboard component in there first off what would the index page be that's important to understand and for the index page inside the app directory we are going to create a file that's why we have a little file symbol here that is going to be called page.tsx let's make this a bit larger this is a name that is enforced by next.js so now when we navigate to slash just slash localhost 3000 slash and then absolutely nothing this page.tsx would be what is shown essentially the equivalent of this if this wasn't the dashboard but just the home page so the component that was rendered out would be this page.tsx which is you know nothing more than a regular react component but it is very important that we name it page.tsx if we wanted to create the actual dashboard then we would go inside the app folder create a new folder in here a subfolder so to say that would be called dashboard and because we created this as a folder this would be represented in the URL so the dashboard if we created a page.tsx in there as a sub file in the dashboard let's copy this name it page.tsx this would mean that we can now navigate to localhost 3000 slash dashboard and the content that would be shown is the page.tsx it is not reflected in the URL right just the content would show up if we navigate it to the dashboard URL and that's the fundamental difference between react routing and Nexus 13 routing while we have a client-side router here this is a file based routing system now you don't need to know this you don't need to remember this or anything we're going to do everything together but it is important I just wanted to explain you the core concept because we're gonna make use of this later it just makes routing way simpler and then for one more core concept that I want to introduce to you this is going to be a different concept this is going to be typescript the most important typescript knowledge that I want you to you know um know for this for this video is going to be how to declare a type let's just say in this example component the code above is completely irrelevant let's say const person is going to be a JavaScript object right and if I press Ctrl and spacebar we don't get any intellisense as to what this constant can have as properties because it's just a random object that we are creating I can pass it anything I can pass it in age of 14 and I can pass it a name of John for example right this would be a 14 year old person called John I can pass this object anything I want the purpose of typescript and the most important thing we're going to be using typescript for in this video is going to be to restrict what this person can contain and to do that we can define something called an interface and we can call it for example again a person let's just call it person interface very verbose and what we can do with this interface is you can restrict this object as to which properties it can have for example let's say a person could only ever have an H of number type a name of a string type and then optionally a person could have something like a job that is optional so we're going to declare it with a little question mark here then a colon and a job would be a Boolean value if they have a job or not now again this doesn't change anything just because we declared the interface however what we can now do is assign this interface to this constant which means that we are saying this person as an object right here is of type person interface and therefore can only have the properties that are defined within the person interface if we try this out we can assign this interface to this object by saying colon after the object name and then person interface thereby we just have declared this constant as in interface of this and now we can see if I hit control in spacebar in this object the only thing that would still be possible to pass for this person that we haven't passed yet for the name and age would be an optional job we don't have to pass it but we can pass it a false value for example if I take this away and also deleted the name this will give us an error because in the person interface we have said there should always be an H property because we did not make this optional there should always be an age property there should always be a name property for every object that is of type person interface and this object right here does not currently have a name property and if I had hit control and spacebar we can see the intellisense the typescript gives us a huge advantage over JavaScript that we can see the name property is still missing we need to pass it and if I try to pass a number it would also know no this needs to be of type string Josh what you're doing is silly this should be any string that we want that's the main typescript feature we're going to make use of in this video declaring interfaces and then assigning them to constant ends to get the full benefit of typescript in this video and with those Concepts out of the way again don't worry you do not need to memorize these we're going to go over everything together but just to make this as beginner friendly as possible keep them in mind and now let's jump into the actual code that's the functionality it's a beautiful chat app super functional and super performant and that's what we're gonna build together in this video we're going to start super slow go from the basic concepts to the more advanced ones go step by step and only introduce dependencies as needed so it's going to be a very easy entry we are going to have let's go to our desktop and first thing we want to do to get started with our chat application is to go into the command prompt this is where it all starts where we are going to create a new next JS application together if you're ready to build something amazing with me let's do it and let's get started right now so the first thing we're gonna do to initialize a new Nexus project is going to be to type npx create Dash next Dash app let me move my mouse so I don't um obfuscate it and then after the create next app we're going to say add latest what that is going to do is initialize our project with an extra s13 the newest version where we can benefit from all the cool stuff they've added you're going to learn a lot in this video Let's click enter or hit enter and that is going to ask us a couple of questions first off the name of a project let's let's just call this real time app YouTube hit enter and then it's going to ask us if you would like to use typescript in my case yes I do you don't have to follow along in typescript though um I will do this in typescript if you want to follow along with JavaScript that works perfectly fine we do want eslint to make our code quality better and we are going to use the source directory meaning all the other directories are going to be moved into one file in our project root and then it's going to ask us if we want the experimental app directory yes we do it's not recommended for production by an extra SES just yet because it isn't stable but it is super cool and I've used it in production several times it works super well and gives us opportunity to use a lot of the cool new Nexus 13 stuff we're going to leave that as default and that is going to create or next.js project for us with all the options that we've just configured and it's also going to install all the dependencies for us so let's give this a hot second to load and then I'm going to be right back when this has finished aha there we go I don't even need to pause the video it has already finished that means I have created that in the wrong directory but moved it to the desktop so here we are okay we can open the nexjs project we've just created with vs code and this is where it all begins Let Me zoom in so you can see easier this is a brand new nexjs13 application if you wanted to we can already start this up yarn Dev and that looks kind of weird but it works so let's go into the browser go to localhost 3000 and let's see what our brand new next.js project looks like if I move that over here this is where we are this is what a default Nexus 13 project looks like um just like a create react app if you've ever used that this is just the thing for next.js I'm going to stop the server and let's do some easy setup first that's going to set the foundation for the project so I've got some notes open here on my right and the original project and the first thing we're going to do is Project setup meaning we're going to install Tailwind CSS which we're going to use for styling and also we're gonna develop a core UI component which is going to be a button that's going to be very reusable follows a lot of best practices first off let's create a Tailwind together or install Tailwind Tailwind CSS is the weapon of choice for styling and we're going to install that with next shares so we can go to the guide on how to do that and literally just copy this Command right here that's that's going to install the necessary dependencies for us and that is going to be tel1 CSS CSS and auto prefixer let's go into our application until the built-in terminal and just paste that command press enter and to make this even more beginner friendly I'm going to use npm throughout this video normally I use yarn but I think a lot of beginners use npm so that's what I'm going to use in this video too and then we're gonna copy the second command which is going to be npx Tailwind CSS init Dash p run that and that is going to create a Tailwind config for us right here great if there is a notification if we want to use a certain typescript version click allow because that's going to give us a bit more intellisense specifically for next year s13 and typescript so if that pops up make sure to accept that and last thing we need to do actually two more things first one being pasting that into our tailwindconfig.js to tell Tailwind which files should use Tailwind CSS styling and secondly we want to put the globals.css um into the we want to put this into our globals.css right these three directives globals.css is created by next.js4 so we don't have to create that ourselves and as you can see it's right here in the source slash app slash globals.css we just need to paste what Tailwind gave us and now let's see if the installation was successful so I've navigated to the main page.tsx let's have a class name of text red 500 that's how we style text with Tailwind say hello world hit save and then let's start up the development server using yarn Dev just to verify the Tailwind CSS is installed correctly now let's go to localhost 3000 and take a look at what this looks like and there should be red text saying hello world if the Tailwind installation went as expected great so that means we have done a good job installing Tailwind and we can get rid of a lot of this stuff we don't need that for this simple function now if you've never worked with nexjs you are new to the file based routing system meaning that the the routing structure is fundamentally different in react we navigate it on the client right and we've defined the routes on the client and next yes we don't do that we have a file based routing system meaning if I were to create a page.tsx that's the naming convention it's called page.tsx it's it's not just a convention it has to be named page.tsx in the new app directory that is essentially a route I have created and if I want to create a route under for example the routing structure we're going to use for the finished application it's going to be slash dashboard slash ad if I wanted to create a dashboard route you would do that in the app directory then create a new folder of what your route should be called for example dashboard and then as I just mentioned inside of that dashboard route we can have a file called page.tsx which is going to be the content displayed on that page and to get the content for this page I'm going to use a little snippet that I'm going to link in the description as well super simple to install and it makes initializing a component easier if you're not in typescript you could leave this away you could leave this and this away and be fine with just the page this is the JavaScript equivalent of what we're going to be doing if you're in typescript you might as well use the snippet it's super convenient to use and if we save that we would expect the service still running great a path to be under um slash dashboard that contains the page that we have just added so let's go to localhost 3000 dashboard and we can see the page we have just created that's how the file based routing system in Nexus 13 works and with that out of the way until when CS is installed let's create one core component that we're gonna reuse across the entire application a button and to do that in the app directory or in the source directory rather we are going to create a folder called components that's where all the application components are going to live and inside of that components folder we are going to create another folder called UI for files that we're gonna reuse across the whole application and in there is going to be our button.tsx that we're going to reuse a lot now I'm going to use the same snippet again you're going to see that a lot in this video so if you didn't bother yet you might want to check out the snippet it's in the description and how to set it up it's also in the description making it easier for you to initialize a new page that's just very convenient or a new component for that sense okay we've created the button component and now I'm going to open up what I've already written out the finished project just so I don't get lost in the source um in this project okay now this button we're going to reuse across our whole application that means it has to receive props based on where we render it because we obviously don't want it to say the same thing every time and if you didn't know there is a method to create reusable button variants meaning like a default button and a destructive button an outline button that we can then reuse across the whole application and the way we do that is by installing one dependency let's go into our terminal stop the development server and say npmi or install and that dependency that I'm talking about is called class Dash variance Dash Authority which allows us to write classes that we can then use wherever we render the button and what did I do wrong no it seems like typescript has released a new version 5 that a lot of npm packages are not compatible with and that wasn't even out when I developed the application a couple of days ago that is a bit inconvenient but what we can do to fix that is downgrade or typescript version we don't need the five it doesn't make sense for us and that's why we are gonna downgrade it the way we do that if you're wondering is we're going to enter the new value right here we can say 4.9.5 which is what class variance Authority expects and then let's go into our command line and enter the command remove dash oops Dash item Dash recurse and then Dash Force underscore oh yeah just Dash Force note underscore modules what that's going to do is it's gonna remove or not module files we don't need those anymore because they are um outdated and that is written with just one U let's remove those that's going to take a while because it's quite a big folder and then the next command we're going to run is the remove dash item so we are removing another item that is going to be the package Dash lock dot Json let's go to remove this file right here great that file has saved or dependencies we don't need that anymore because we're not gonna downgrade our typescript version we've already entered the new value in here which is 4.9.5 which means no we can run npm install that's going to take a look at this TS config.json and install all the dependencies with their versions our case that's going to be typescript 4.9.5 and there is no it's not the worst than typescript 5. it's super inconvenient that they just released the new version because I think a lot of npm packages depend on that and have not done that upgrade yet so in this sense it makes um in this case it makes a lot of sense downgrade or typescript version great and now we should be able to install npm install Clash there's class Dash variance Dash Authority press enter and let's see if that works seems to work we install that package successfully great okay so we got that problem out of the way that was unexpected but we solved it very well okay what class variance Authority allows us to do is Define variants of our button you can say button variance is going to be equal to and then c v a that's a function we're going to call and import that from the npm package we have just installed now as the first argument the CVA takes are props or styles that are always applied to the button in our case that's going to be active a scale of 95 to make the button a bit smaller when it's pressed and inline Flex and items items Dash Center to Center the items vertically and they justify Center to Center them horizontally a rounded of medium and we close this down a text of small a font of mid oops medium and a transition of color and let's see if there's anything else yeah there's a few more we want to pass in here the focus when this element is focused that's a pseudo class we get access to the Tailwind we can say the outline should be none the focus when that is active there should be a ring of two that helps with keyboard accessibility that's why we do this when the button is focused we want to have a ring of slate 400 that's just the color that is present when we navigate to that button with a keyboard for example and when we're focused we want the ring to have an offset of two just to give it a bit of spacing when the button is disabled we want to make that apparent to the user by giving it an opacity of Dash 50. and then the last thing when the button is disabled we want it to have a pointer Dash events Dash none so the user cannot interact with it I know that was a long class name that's one of the exceptions in this application I try to keep them short this is just one very long class name okay and then that's the props or the styles that will always get applied to this button now what we can do is Define variance of this button and when we use the button I think it's going to get very clear what these are but we can declare them by adding a variance property inside of the CVA and inside of the variants we can declare whatever we want so let's say the default button for example right what what happens when we don't specify a variant it's going to use this one right here and normally the button should have a background slate of 900 a text of white when we hover over the button it's going to get a background slate of 800 and when we are actually no we don't need to we don't need to specify dark mode I think we're fine on that that's going to be the default styling offer button and then let's create a ghost button so the button is not going to be very apparent to the user but when we navigate VIA keyboard it's obvious and The Styling is also going to look good so it's just going to be a bit more subtle we're going to have a background of transparent for the ghost variant then a hover of slate no that's gonna be text Dash slate Dash 900 and then when we hover over the button it's going to get a BG of slate of 200. right and that's it for the ghost button we can leave it at that for now we don't need to worry about the other stuff and then for the size we're going to have three options and by the way these I made up myself right this is not enforced by CVA you can name this whatever you want and give it whatever properties you want that's the whole point the default size of the button is going to be a height of 10 a padding y of Two and a padding on the x-axis of 4 then for the small variant of this button this is going to get a height of nine padding on the x-axis of Two and a rounded of medium actually that's already applied through the default prop so let's just leave that out let's just apply the height and the padding and then for the large option it's going to get a height of and we need to put that in quartz obviously a height of 11 padding X of 8 and that's it great now this is still all red and we still need to define the default variance default there and we need to declare that right here we can see default variance Aha and I've done a little mistake I now know what's wrong so the variance needs to take a custom name right the variance is enforced by clusterian's Authority what we can put in inside of the variant are the certain variants that we want but these names that's what we Define ourselves and this one as well but this one is enforced by CVA and then similarly we're going to move the size up a bit into the variance so it's on the same level as the original variant that was the problem and then we can Define our default variance not so because this is a custom name class variance Authority doesn't know this should be the default even though we named it that so we need to declare that separately inside of the default variants inside of CVA and the way we do that is if you work in typescript this is super convenient we now get intellisense on what properties we can pass the variant is going to be the default obviously and then for the size that we can also pass that is obviously also going to be the default size and that's it now this might seem a bit cryptic at first why are we doing this but it's gonna get very apparent to you whenever we use the button anywhere throughout our application this is super convenient and then we get the actual bottom component right now if we render the button out just the div would be shown right this itself does not do anything we still need to create the button and if you're in typescript there's a certain method we can do that by we're just going to make this fully typesafe to do that we're going to interface export an interface so I'm going to remove the original one from the snippet I use and declare it down here and we're gonna export in interface called button props and inside of the button props that's what we're going to use for whatever properties we receive in the button so we get intellisense wherever we use this button on what we can pass to it so we don't get any error that's the beauty of typescript and the these button props don't take an object directly but they extend a certain other interface that is going to be the button HTML attributes that we get from react and inside of here we can pass the HTML button element meaning whatever props we pass to the button is going to include whatever we can pass to a default button in react um I think it's going to get apparent if I just demonstrate that to you really quick so if I were to save the button and then render it out right here on the main page button we can see we can pass everything that we can pass to a normal react button that's what we just did by extending that object and that is going to extend one more thing so we're going to add a comma and that is going to be the variant crops that we can't automatically import but we get those from class variance Authority so we can say variant props import that and what that's going to do for us is allow us to also get intellisense on the button variants that we have declared up here so the variant props takes a generic meaning kind of like a function but with a different syntax it takes the type of and then button variance so the variance we have defined above that's it and now what we can see what that did for us by the way I still need to put an anti-object right here so it's not going to give us a type Arrow what that does for us is now we can see we can pass it the size and we can also pass it a variant cool so if you pass it a size and take a look at what that can be it can be default large or small so whatever we have defined inside of the button as the button variance how convenient is that right and then in here inside of this object we have at the end we can pass custom properties for me that's only going to be one is loading of Boolean so we know if we should display a loading State inside of this button great so in terms of typescript and setup this button works perfectly however it doesn't render a button yet so that's not ideal it just renders a div let's fix that and let's get some props from this first we can destructure props meaning we just take them from the original props the variant we want the is loading so we have the class name children variant and is loading that's what we're destructuring we also want the size and then lastly we can set a catch all so all other properties that the password this button are going to be caught in this dot dot props right here and we can just initialize a new button and pass it all of the other properties because we know they are properties that can only be passed to a react button because we put that right here so we can safely pass those into the button we want to give the button a class name and the class name I'm going to leave for last because that's going to require a small utility function then we can pass a disabled of equal to is loading so whenever this button is loading it should be disabled we can pass the props as I already mentioned and then inside of the button if the button is loading we want to display a loading State the way we do that is by doing a conditional render so if the button is loading we are going to show a loader 2 icon that doesn't exist yet like that and else if it's not loading we're not going to render that icon Now where's that coming from that comes from a dependency a super nice icon dependency and we're going to use npn install to install that dependency called Lucid Dash react Let's Lose It Dash react press enter that's going to be the source of our icons and that's going to be quick to install it's a lightweight dependency we can now import the loader too and to actually make it spin we can give it a margin right of two a height of 4 width of 4 and animate Dash spin that's the Tailwind animation it's built in you can see that it takes one second to spin around and just looks good out of the box and then we can render out everything else that is passed to the button as children so for example if we had a button in the page.tsx component and I typed in hello inside of the button this would be passed as the children and to our button component that's what we're rendering out inside of the button and that's it we can save that and now we only need the class name now the class name is going to be conditional so we need a little utility function for that and where are we gonna initialize that is in our source directory let's make a new folder and let's call that lib lib is important because it's the folder for preparing libraries for or application so inside of this lip folder let's create a file called utos.ts utils are just utility functions that help us throughout our application and the one we're going to create is literally three lines of code it's super simple we're going to export a function and call that CN for class names and this function takes any input right any amount of class names and that is going to be of type class value array if you're in typescript no what the hell is this type Josh where is that coming from and to make this utility function we are going to install two dependencies with npm of course that are super convenient one for conditional class names and then the second one to merge Tailwind classes together so the conditional class one is going to be clsx that's what that dependence is called it's going to allow us to do conditional class names super convenient you're also going to see that later in the app and then Tailwind Dash merge it's going to optimize or tell when code automatically merge classes that are redundant together for cleaner Tailwind code and now we can import that class value from clsx great and the only thing this utility function is going to do is it returns a TW merge that we get from tail merge to optimize the table code and then what are we going to merge a CL SX to do conditional classes and pass that the inputs great now this doesn't exist yet and we import that also from clsx press Tab and we call that input that needs to be inputs and that is our utility function done so anytime we create conditional classes throughout our application this is the function we are going to use let's go back into our button component and Implement that in practice right away so the class name is going to be conditional so in curly braces and then CN import that from libutils and then a button variance and that button variance those are the variants we have declared up here remember inside of there we're going to pass an object and just pass along what we have restructured from the props that's going to be the variant the size and then also a class name so what the CN does for us is if we want to overwrite Styles anywhere we render the button we can do that it doesn't always have to look exactly the same even if it kind of should for you know reusability purposes it should always look dissimilar but if you wanted to make the background slate of 800 somewhere for any reason we can still override the Styles and that's why we have the CN function down here and that's the button done that's all we need to do and literally we can close out of that file and that lays a very important groundstone for or application okay so let's do a very quick summary that was chapter one The Project setup all we need to do um was create a button initialize the project and install Tailwind those are the core components that we need to move forward if you're following along so far good job champ and we're gonna pull right ahead with chapter two the database setup database is going to lay the crownstone for our application it's super important we're saving authentication data in there the account data right we're saving the chat data in there the friends the friend requests and so on it's the Cornerstone of our application so that is why we are going to do that second to get started with our database we are going to go to up stash upstash is a provider that manages redis for us it allows you to scale easy and it provides a super fast redis database this is the original application and if I log out of this account I can log in with another one that's going to be the support at wordful.ai to create a database there now if you don't have an account yet you can just log in via Google for example or any other provider and redis upstash redis is gonna be what we're using for a database super fast as I said because redis gets its data from the cache right from memory meaning it's stored in a JavaScript constant you can imagine it like that but it's also being persisted in the back end so redis even though many people just use it for caching is a full stack or fully fledged database I should say that makes your app super fast and that's very important in a chat application it can handle stuff that MySQL can't handle in the speed and that's why we're using it so op stash is by the way as I said in the beginning kind is sponsoring this video which is super convenient and inside of Ops Dash let's create a new database great so we can give it any name let's call it real time Dash chat Dash YouTube that's going to be a original database and let me zoom in so you can see this easier the region for me is going to be Frankfurt this should be closest to your users wherever you expect your users to be and then we are going to enable the TLs SSL and as you'll notice upstash makes it super simple to create a database it's just loading for a few seconds and that's all we need to do there's your database how easy was that and then we are gonna copy the two values you see down here the up stash red is rest URL and the upstairs rest token that's what we're going to need to interact with this database let's copy the name of the first one first let's go into our project and these values are sensitive information if you don't know sensitive information should never be hard coded as a string in your app but rather you do that in a DOT EnV file or you can call this Dot env.local which will be automatically in the git ignore meaning if you're using git as Version Control you won't push that up to your repo so anyone can access it that would be horrible we're going to Define it as upstage redis rest URL and just copy the value that upset gives us and just paste it in here into the env.local and the same thing for the other variable which is the up stash that is rest token we can paste it in here and as the name might suggest this is what we're going to use to interact with the rest API of upstash copy that value as well and now we have two values in or dot EnV that we got from upstash the rest URL and the rest token we can save this and now we can actually get started with the database and to do that let's create a db.ts file and if you've listened closely you might already be able to guess where this database file goes because after all it's just preparing a library for our application meaning it will go into the lib folder it's called this db.ts I'm not naming it redis.ts because this should be a bit more unspecific if you wanted to switch out to a different database later on then you wouldn't have to worry about renaming the file for example it's just a bit more generic and then here we can import a redis class from at oops that needs at upstash slash redis which is a dependency we haven't installed yet so let's go into our terminal and say npmi at upstash slash redis hit enter and they're going to make it super simple to connect to our database we just need to install that package and then we can export a constant of DB and that DB constant is going to be a new redis class we are going to pass that an object and if you are wondering what this red is text it takes a URL and I'm not getting any intellisense right now which is a bit weird but the URL is equal to the process.env and let me close the other files just so it's a bit cleaner the process.env and if we take a look at the EnV right here it's the upstairs rest URL we can just copy that paste it here into our database file and same goes for the token which is a process.env DOT and then the up slash red is rest token paste it in there and that's all we need to do that's our database and let's try out if that works um yeah let's just try it out let's give it a shot um let's make this a async function home and whenever we navigate to the home page let's say we're gonna await a DB import that from the file we've just created dot set and we're just gonna say we're gonna set the hello to hello and so if we navigate to the page we should be able to put a value into our database and by doing that npm brand Dev we can verify that we've done the database integration correctly or if I messed something up so if we go to the main page let's see what happens let's load that and hopefully everything goes right we do see the page and now let's look into our database to check which values we have in the database it's super easy we can go into the data browser and great we can see we have a string of hello and the content of a lot so this is just a key value pair that we are storing in redis just like that so we've done the database integration correctly really really good work so now that we've done that we can actually get started with the authentication which is a key component of our app everyone needs to be authenticated so they can chat and wrap so authentication super important and we're going to use next auth for that if you don't know what next auth is next auth npm it's a package that allows us to implement authentication of any kind like social media authentication with GitHub or with Google in a super easy and secure way encouraging a lot of best practices that there are when working with um you know full stack web security it's it's super cool this is what we're going to use and to install it we can just copy the command or type it out npm install next off let's close out of that and get started with the Authentication let's stop the development server yes I want to stop that paste that command npm install next dash off and then that will take a little bit and we can actually get started with the Authentication great it's done and even though for all the API rods in our app we're going to use the brand new Nexus 13.2 route handlers there is one thing about next auth and that is it doesn't support that yet so without any disadvantage we can just initialize it in the pages folder just like we would in the latest version of Nexus the Nexus 12 one where we initialize a file called pages and inside of there we're going to create a folder that folder is going to be called API API so Pages API and then another folder called auth that naming structure is required by next auth and then inside of here we can have a catch all segment by doing like an angled bracket dot dot dot for a catch all and then a next auth close that with a bracket again and then dot oops dot TS meaning any request that is going to be sent off is going to be handled by this file right here and there's only going to be five real lines of code in this file let's export default next off and pass it something called auth options now the auth options doesn't exist yet but we can import the next auth already and the reason we're doing this is because technically we are just preparing a library for our application so that shouldn't be handled right here in the API itself but that should rather be handled in the lib folder and let's just leave this file with an arrow for now because the auth options are what we're going to create in the lib folder right now let's go into that folder the loop and then create a file called auth.ts and this file is going to response be responsible for all the authentication that we do in our app okay and here we're going to exports export a constant called auth options which we are going to use in a lot of places in our application this is super crucial and that is going to be if you're in typescript of type next auth options now the automatic Imports don't seem to be working and what you can do in that case if that ever happens to you you can press Ctrl shift and P and reload the window mostly that fixes the automatic Imports so let's reload the window that's going to initialize the typescript features again and now we can see the automatic Imports are working great and that means we also get type safety on what this object or constant can take super convenient first thing this is going to take is an adapter and what an adapter is it means every time somebody calls this authentication if they log in with their Google account for example a certain action with our database will be taken automatically in our case meaning the user data will be put into the database automatically like the email and their ID and so on so we don't need to worry about that and the adapter is going to be one we can install there is one made specifically for redis and upstash which is super convenient so we can say npm install and then add next Dash auth slash up stash Dash radish Dash adapter hit enter on that and that and so every time somebody logs in and trap the database interaction will happen automatically we don't need to worry about auth data persistence whatsoever now we can import that adapter import oops no that's not the one that not what we want to import it's called up stash that is adapter and we can already see the automatic import right here and what we cannot do is put that into the adapter and wrap our database with it that we can import from the database great that's all we need to do now for this session let's have a strategy called JWT you can see there are two options here database in JWT JWT means Json web tokens meaning we don't handle the session on the database which allows us the reason we're doing that is so we can verify the session in middleware later on to protect our routes way more easily than we could if we kept the session and database and then for custom Pages we want we want the sign in page to be equal to the slash login path so we can define a custom page for that it just looks better and now is the time to think about what we want our application or what we want our users to log in with like email password social media Google GitHub what do we want and then in the case of this application what we want is the Google and we initialize that as providers by the way that takes an array and in our case we want the Google provider because we want to be able to log in with Google and next.js for next auth already gives you those under import Google Provider from the next dash off slash providers and then we see a folder containing a lot of providers right auth zero Azure box bungee Cognito AWS service email Facebook GitHub and so on we want Google right so we have a ton of options in that regard and the Google provider takes two things if vs code finished loading you'd be able to see that I had that issue in the previous um long form video I did vs code sometimes just takes forever to load so let's give this a hot second and then you'll be able to see what the Google provider takes I'm going to leave you with a little Cliffhanger and be right back aha there we go so it takes a client ID and a client secret that's the that's the Cliffhanger so first off being the client ID and for that we are gonna put that in an environment variable as well but we want to know if we forgot to Define this variable so what we're going to do it's a best practice in my opinion is write a function that gets us those values and logs an error if we don't set them so we know what the error is in production when we forgot to set those values let's call that get Google credentials and it does what it says of the can it gets us or Google credentials which is going to be a function and the cons client ID let's call it that because Google calls that it's going to be the process.env.google underscore client underscore ID and then the const client secret that's the secret code we also get from Google we don't make this stuff up ourselves it's going to be process.env.google underscore client underscore Secret great and if we don't have a client ID or if we don't have a client secret then I'll actually let's let's do this differently so if we don't have a client at the or the client ID dot length is going to be equal oops equal to zero then we're gonna throw an error Pro new error and that needs to be capitalized because that's a class and we're going to say missing Google underscore client underscore idea and then let's copy and paste that with shift alt and arrow down the exact same thing but instead of the client ID we can paste in the client secret with client secret.length and then missing Google client secret just so we have some sense of what's going on if we forget to set those and if everything is fine let's return an odd let's return oops let's change that let's return an object with the client ID and also containing the client secret that we can then use right here for our Google provider so the client ID is going to be get Google credentials invoke that and now we have access to whatever it returns obviously we want the client ID for the client ID and then similarly for the client secret that we also need to pass it's mandatory from Google we're going to invoke this function once again and pass the client secret that way we have amazing sense of what's going on while we're deploying it if there's any error then we can monitor that using the function we have to find up here great and then there's one last thing that we can pass inside of the auth and then we're done with it and that's the callbacks callbacks are actions that are taken when certain events happen that next auth detects it goes down here the callbacks it's an object and we can pass it you know the JWT the redirect the session whatever should happen when someone signs in and we're going to use three of those first one being the async JWT function that we want to pass inside of the JWT we get access to the Token a few other stuff we just want the token and the user from next auth it's automatically provided super convenient and then it's highlighted because we're not returning anything yet and the first thing we want to do is check if there's already a user in our database so we can say cons DB user to determine if this is a new user or not and we can await that's the reason we mark this is asynchronous the database that we've already defined and Dot get and inside of here the we are going to apply a specific redis naming convention meaning we have the original name like user or chat or messages then a colon and then the variable value so in our case that's going to be the token ID that is automatically generated by the adapter that we have up here the Ops Dash redis adapter that takes care of the whole ID Generation stuff so now we can just see if that user is in our database and once we get the auth working you'll be able to see just what we're doing here right now we can't log in yet but as soon as we do you'll know what this exactly is doing if this was a bit too abstract for you and the type is as user or null now the user type doesn't exist yet so this is marked this any cannot find M user and the way we Define the user type what one user looks like is through a specific folder and let's call that types inside of the types let's have a file called DB so any types that have to do with our database dot d dot TS the D dot TS is just for definition typescript files meaning you don't have to import them anywhere they are available throughout your application and the user interface interface user this is how we Define a type and typescript it's very simple the user will have a name of type string the user will have an email of type string the user will have an image of type string because that's going to be the URL and then the user will have an ID that is also a string great and as you could see because we defined that in a DOT d.ts file it's already known to the um to this file now we didn't need to import it and what we want to happen if there is no DB user is we want to get the token dot ID and assign that to the user and we know that exists dot ID and this exclamation point is just asserting that we know this type exists the typescript and this might not be the case actually this might throw an error if we're logging out but it doesn't matter and then we are going to return the token so the token that we're returning is what is set as the JWT afterwards and if there is a DB user if this user is not new then the ID of the token should be the DB user dot ID similarly the name should be the DB user dot name then the email should be the DB user Dot email and then you can probably guess with I'm going to call this picture you could call this image as well it's going to be the DB user m dot image naming really doesn't matter for that for that case so the jwtu respects us expects us and maybe it respects us as well for using it I don't know it expects us to return a JWT value that is then stored for the session token for the user so in our case it would contain these values right here that's what we're doing and then after the JWT callback let's have an async session callback whenever a session is generated that's what we get access to through other application when we verify if the user has a session we get access to a bunch of properties like token user and session we only want the session and the token we get provided that from next auth and inside of this function if we have a token then we want to use those token values in our application right so we need to set them to the session values that we can then access throughout our application because we can't just access the token itself so let's append them to the session by saying session.user.id is going to be equal to the Token dot ID now we don't get intellisense for this right now because we also need to define a separate types file for next off so we could continue working right here let's take a one minute aside and create a new types file so this will be way easier way more enjoyable to work in let's call this next auth.d.ts and we're going to define a few custom next off types in here um so first one type user ID is gonna be a string so this is an alternative to an interface it's a type it does pretty much the same thing and then we want to declare two modules so we get some intellisense in our auth.ts file first one we want to declare is we can say declare module and then this is called Next dash off JWT so these are very values specific to the Json web token this takes an interface JWT and in here we're going to say the ID is of type user ID or string right and then secondly we want to declare the second and last module for this file is just next auth so anywhere we use next auth we want to assert that there is an interface session and inside of this session there is a user of type user now we've already defined that so typescript will know what we're talking about and we are adding one value to that that's why we have this syntax right here with the and object the ID of user ID and that's all we need to do now these uh we probably still need to import at least edit in the example folder so we're going to say import and we're only going to import types so we can say import type and then session end user from next dash off and then we also want to import oops import type JWT so typescript knows what that is from next dash off slash JWT that's where we get those types from we can save the file that's all we need to do and now we can see if the arrows already went away in our auth.ts file because now um or application North knows what the token can have right so the token could have an email an ID name picture and sub okay then we are going to continue appending those values to the session so we get access to them throughout our application so the session.user.name is going to be equal to the Token dot name the session dot user dot email is going to be the token dot email and then lastly the session dot user dot image is going to be equal to the Token Dot and we call it picture here I guess it also makes sense to call it image there great and then we need to return that session if there is no token right so we always have to return something if there is a token or not that's how this function works and then lastly the last thing we want to do um is in here when a user has signed in we want to redirect them we do that with the redirect and this expects to return a string to redirect to then or case that's going to be the slash dashboard and that is authentication almost done we just need to import that and that's authentication done I think in the next step it makes sense to try out if this is all working and take a look at how this gets put into a database automatically okay before we try out the auth I realized since I want to make this super beginner friendly and also understand I want you to understand every step of the way let's quickly take a look at what we've done so far I think that will make a lot of sense for example the button right I mean we did the button but we never took a look at it and again the Imports are not working that's why I'm gonna click reload window and then hopefully once the tab script features have initialized the Auto Imports are going to be working again button no that doesn't seem to be the case button there we go um components button hello let's just have a button that says hello start up the server and now let's take a look at the button really quick just to even see what we've done so far it's reload the page wait until the dev server has finished loading and there we can see the button now I'm zoomed in a lot but here you can see the button the default button when I navigate with the keyboard there's a little ring on it that's what we did with a button component and now we can also render that button in a different variant so we can pass it a variant of for example ghost that will turn that button into a ghost button that is only visible when you hover over it but still perfectly accessible via keyboard great that was just very important for me to show you and also one thing um okay that's the finished code I'm going to move that over here one thing um I want you to understand if you're completely new to typescript um maybe the auth options were a bit fast so what we are doing here is assigning a type to the constant meaning that we are saying this constant has this type right here we do that with a colon and then listing the type and the auth options are given to us right we don't make them ourselves but if we for example go into the page.tsx and that initialize a new constant called user then we can pass anything in here this constant can be whatever we want but if we Define the type of it then that means we are restricting that constant to B of A certain type for example if we made a user mock type and say a user mock any object that is of type user mock always has to have a name property of type string for example right and then we say this user that we initialize right here which right now can take anything if we declare that as a user mock then we can see a name is required if we hit control and spacebar that's why we're doing the whole type thing right that's what you see in the auth.ts it's just so we know which types this can the auth options can have and we get type saved right so if I try to pass something that doesn't exist of type false we would get an error that doesn't allow us to do that because with this type we know what is expected and the syntax for that is just with a colon I just want to make that clear I think it's very important for this to be totally beginner friendly and now we can actually move on with a checking if the authentication works and then B we can actually implement the login screen I think that makes a lot of sense and we can simultaneously take a look at the data that will be created in the database for or authentication so first up let's create the login route where users will be able to log in if you I mean I I mentioned it in the beginning of the video Nexus has a file based routing system meaning if we want to create a new um and your path in the browser like slash login we do that through a folder that we call login and inside of that login lives a page.tsx that name is required and for this to be a page whereas the login we could name anything and that would represent a result I'm going to use or handy functional component in here and let's quickly create the login route for this so what the login is going to do is there is going to be a button and with the button the user is going to be able to log in right and in next year s13 these components are server components by default a server component cannot have any interaction with the user meaning if I try to pass an on click right here and we just log out something to the console like hello and we go to that path like slash login we're going to get an error saying this can't happen in a server component right here event handlers cannot be passed to client component props because by default this component is a server component we can't have interactions therefore we have to declare this as a client component because we want to have some interactivity on it right that's the reason we're declaring it as a client component and the reason behind server components themselves is they're faster right we render all the stuff on the server therefore we are not shipping that to the client that's why server components are actually super useful let's develop or login page let's start with a fragment meaning in the actual browser nothing is rendered but in react it counts as a single child and then in here goes a div now there's going to be some styling involved in this if you ever get lost make sure to visit the GitHub repository and let's start sliding this so there's going to be a div and that div is going to have a class name of flex and minimum height of full items Dash Center to vertically Center them and justify Dash Center to horizontally justify these um in the middle to Center them a padding y of 12 a padding X of 4 on small devices we want a padding X of 6. there are large devices we want a padding X of 8 just to make this a bit more responsive and here it's going to be another div with a class name of width of four a flex Flex column so we're aligning them vertically instead of next to each other that's why we have the flex column oops I didn't want to remove that we're going to Center them with items Dash Center and a maximum width of medium meaning this div cannot exceed the certain width of 448 pixels then give it a space a space y of eight meaning all elements inside of this div top level elements so for example a div right there and then a div right here this would be the top level diff and this wouldn't be affected by the Space X of Y only the top level so if I had another div these two would be the top level and therefore they would both be separated by the space y of eight okay inside of here is going to live another div with a class name of flex Flex Dash call for Flex column items Dash Center and a gap of eight great now in here we want to render out our logo however we don't have a logo yet I'm just going to say logo for now and then we're going to implement the low the the actual logo together in a second and below that it's going to be an H2 saying sign in to your account now with Tailwind the H2 is good for semantic HTML but it has no inherent styling so it looks just like regular text that's why we also style the H2 with a margin top of six to separate it from the top text Dash Center text of 3XL so it's very large a font of bold tracking Dash type to make the letter spacing a bit less and then a text Dash gray-900 for the styling of this H2 great then after one closing div we are going to include or button component that we created as I said we're going to reuse it a lot and also in this component right here this button is going to get an is loading of is loading now that doesn't exist yet we're going to create that in just a second actually we can create it right now it's going to be a state a react State and we're going to call that is loading of type Boolean and that is going to be false by default now if you're wondering this syntax right here is typescript specific if you're in JavaScript you can leave it out even if you're in typescript you could leave it out this is just for explicitly marking the state as Boolean typescript would actually infer that even if we left it out this would be a Boolean I just like keeping it in there though um just for you know overview then inside of the button the type is gonna be button and a class name we're going to have a class name in here and let's put this into the new line just like that and the class name is going to be Max width of small and MX of Auto to Center this um horizontally a width of full and a BG of slate 200 just a light background and actually let's not do that I think the the original button component already looks good let's exclude that slate part and then on click whenever we click this login button we want to log in with Google Now that function doesn't exist yet let's create that up here function login with Google is going to be an async function because we oh and I am trying to combine two syntaxes here that doesn't work let's just have this as an async function log in with Google like that and now the error is gone for the on click great we're going to worry about the actual logic in a second let's finish the styling first now for what's about to be in the content of the button um navigate to the GitHub repository because instead of typing out the SVG all by herself we can just copy paste that it doesn't make sense to just stupidly type out an SVG so we're going to go into the source directory and then under the app into the login page you can see the page.tsx and from here we can copy the SVG that is the Google logo as I said no point typing that out itself you can paste it in here and then right below that we can say something like Google great Okay so let's save that for now and that is a big part of the login page already done so let's see what happens let's navigate to our login patch press enter and see what happens we can see logo sign into your account and then there's the Google button beautiful now we don't have a logo yet we're going to fix that sign into your account and then the Google button that does nothing right but it is super accessible via keyboard because we used our reusable component however when we are logging in um something should happen right nothing happens yet and let's Implement that next to actually log us in okay so inside the login with Google this is an async function we want to have a try catch block now we try catch is super useful for executing asynchronous code because if anything goes wrong during the try stage the catch block is going to catch the error and we can inform the user of the error in here we're going to handle the login functionality before that we're going to set is loading to true to actually show the user okay something is happening and you are being logged in or forwarded and to actually display that to you to the user we can't just set the state the user doesn't see the state instead we want to go into a button component and then if it's loading we don't want to show this SVG the Google logo and if it's not loading we want to show it so let's cut the whole SVG and then say if it's loading we're gonna show null else we're going to show the SVG what this is going to do is if I just mock the loading set by sending this to True by default see what happens the Google logo disappears and a loader appears instead so it looks really nice as an alternative to the Google logo right so there's no loader additionally to the logo but instead of it it just looks super nice it's a nice attention to detail and now we can actually get into the authentication logic which is super simple by the way we can await a sign in and that sign in is actually being provided to us by the library we are using the authentication for or with is provided to us by the by the library that we buy next off okay Jesus Christ and we can say import sign in from the next dash of Slash react and what we now need to do is just call this function and pass it the provider we want to log in with in our case that's going to be right here Google we want to log in with Google and then as a catch block we want to display display error message to user right now we don't have any means to do that but we're going to add one in a second it's very simple and then finally no matter if we're in the try or the catch no matter what happens we're gonna set the is loading to false again after setting it to true in the beginning now vs code is loading again let's give it a hot second it keeps doing that I don't know why um but it's going to be easier when it's finished loading so let's give it a second I'm going to be right back depending on how long this takes okay so it finished loading let's format this page and now work on displaying error message to the user if something goes wrong and how we're gonna do that is with a library and I didn't want to do that let's stop the server for now and npm install react Dash hot Dash toast it's a super famous library for handing toast notifications in react and you're going to see exactly what that means in a second when we Implement that now to implement it we're going to go into our app folder and then we are going to create a new component no that's here under components that's where we want to create it called providers providers dot TSX now the reason we are creating this component let's initialize it is we need to render context in Nexus 13 as a client component context is a client thing right if you don't know what context is it's a react hook that provides some kind of state to the entire application and that can only be done client-side not server-side that's why we need to wrap this provider that we're going to use for the toast notifications in a client-side rendered um in a client-side rendered component and how we do that is right here with the div we can actually take that away just have a fragment so we don't unne necessarily put something into the Dom that we don't need and then here we're going to have a toaster that we get from react toast essentially allowing us to display its host anywhere in our application if you don't know what it host is yet don't worry you're going to see that in a second with a position of top Dash Center and a reverse order of false we don't want that and that's it that's the provider now the providers is also going to receive the children right because we're going to provide to the whole application that means the whole application will be passed as children that we also need to render out else nothing would be shown in the application if we didn't render these children if you're in typescript these children are of type of react node that we get from react great and now the question is where do we Implement that provider and the answer is we do that in the root layout so in actual s13 a root layout will be automatically generated for you and we can use that to our advantage by wrapping the whole application that is these children right here because we're in the root layout everything will be handled through here we can wrap everything the whole application with our client-side rendered providers meaning everywhere except in this component right here which is totally fine we will have access to send tools notifications and as long as you've got the toaster in the layout you're golden that's all we need to do we can close all of that and now we can display an error message to the user we can do that by calling toast and we get toast import toast from react hot toast we can import the toast and the toast when we put a dot has multiple options we can pass for example the toast. error if we want to display an error message and then the message is going to be something went wrong with your login for example whatever you want to pass as a message right let's save that and go back into or browser to see if that works right let's scroll back reload the page whenever the server is done loading in the background and this page will be reloaded and then let's try out the login technically it can't work yet because we haven't entered our Google credentials but let's see what happens okay great missing Google client ID great now we actually get good intellisense on what is happening and why this is not working and we can see if the toast is working by just doing something that won't work for example actually we could just throw new error doesn't matter so we will never get to the side and Stage we just want to see what happens when we force the error we can mark that so let's click the button and that is the toast notification I was talking about so if something goes wrong it's going to be displayed beautifully to the user and they know what's going on that's why we have the toast notification in here you can remove that and now let's get our Google credentials to actually handle the login right for that let's go back in here into our auth.ts and see what we named these variables so first one is the Google client ID let's go into our DOT EnV that's where we're going to define the secrets the Google client ID and then also we want the Google client secret copy that from the auth.ts paste that in the envy.local and now the way to get them is pretty straightforward I'm going to link you one or multiple very good videos in the description explaining that I can't really show you that because for me there's a lot of sensitive information in my Google Cloud console regarding the the company I'm developing so it's kind of hard for me to show you that but it only takes two or three minutes it's very straightforward and Google is going to tell you these values exactly like the Google client ID and Google client secret look into the description I've linked to videos and then when you're done um following that video or those videos you're gonna get a Google client ID and a Google client secret I'm just going to copy and paste mine over from the last application I did just so we can see the login Works pause the video now get these two values Google client ID Google client secret and then play the video when you got those values okay so I'm going to assume you got them now let's I'm gonna paste mine in here we can save the dot EnV and we need to restart our server because we changed EnV values and that isn't implemented in hot reload we actually need to hard restart the server okay so let's go back into the server to our login page and see what happens let's reload this and click login with Google and that should prompt me to log in with Google Now great that works and we have the options of our normal Google accounts I'm going to press admin and now it should redirect us to the dashboard because that's what we configured in the auth options in the auth in the lib file right as the redirect as we are on the dashboard great so the authentication works just as expected now if you have any errors if you encounter anything that shouldn't happen make sure in your Google Cloud console you define localhost as an authorized source and later if you deploy the application then that URL that you deploy to should also be an authorized source for Google otherwise you'll get an error during the login stage other than that all should go well and now we are on the dashboard with the authentication working great let me look into my notes we got the authentication working that means we have laid the groundwork for everything else so what the authentication allows us to do is for example let's actually close all of this just to clean this up a bit and then let's go into our dashboard page how we authenticate the user is the following we can say const session is going to be equal to a weight and this needs to be marked in some asynchronous component for the away to work like that and get rid of the FC we'll just have a normal page right here await get server session that we get from next auth and then here we're gonna pass the auth options that's why they are also important not just for the initial login but also for getting the session and then let's render out that session in a pre pre is meant for code if I'm going to remove it in a second you can just watch at this point no need to follow along json.stringify I just want to demonstrate to you what the session looks like at json.35 sessions so we're displaying that as a string and when I reload the page um okay we don't get a session right now I wonder why that is let's log in again with our Google account and it seems like the authentication is not working correctly let's see what happens in the console decryption operation failed so something is still not good with the authentication I'm gonna go and check what that is and then I'm gonna be right back with a solution okay not long after I think I know what the issue is so we need to go into a.nv.local and Define an environment variable called Next auth underscore secret and we can we can put anything here that is going to be used to encrypt or Json web tokens and we get the error of decryption operation failed because we don't have that decryption token yet so I'm just going to say super secret save that for now if you actually wanted to create a secure key for your application you could go into your command and if you have something like open SSL installed you can run open SSL gen rsa2048 run that and that's going to generate you a super secure um key that you could use for this um next author secret to assign your Json web tokens with or if you don't have openssl you can either install it or just Spam something on your keyboard and that should also work for now at least it's not super secure but it it works and let's restart the server because we changed an environment variable and then tried to log in again and hopefully that will then work so let's go back to the localhost slash login and verify that it's working now let's login again and there we go the session is working we can see the user the name my email the Image store content which is going to be very helpful for the chat later on then a user ID and now I want to show you something you remember our database right because of the adapter we have in the auth if I reset this you can see the user data was automatically added to the database which is super convenient right so we have the user account by user ID with a Google account we can see all the Google details like when it expires the access token and so on that's not going to be super relevant for us way more relevant way more relevant is this right here we enter the user and then the user ID and get back the name the email the image whether the email is verified we don't care about that for now and the ID and then also we can search by email and get the user ID super convenient that's what the adapter the up stash adapter we put into or both.ts this one right here that's what this one is doing for us very very convenient and now we can verify the session throughout our application how cool is that great now that we've got the authentication working next up I think it makes sense to handle the sending of friend requests because no chat can happen if we don't have any friends on the app so I think first up it would make sense worrying about that just the sending of friend request functionality now to keep this project a bit more organized there's a certain routing structure that we can apply at this point it didn't make sense to introduce it to you before but what we can do is create new folders and wrap them in these parentheses and those won't be reflected in the actual the actual URL those are just for organization so I'm going to move the login into the in parentheses auth that's just for organization and for this app and that's going to come in super handy right now if we create one for the dashboard as well wrap it in parentheses because the dashboard is going to have multiple pages right it's going to have a layout only for the dashboard it's going to be the index page for the slash dashboard but also something like slash app where we add users right and that's what we're going to implement right now so inside of the dashboard let's have a add folder that's going to be the actual URL because of the file based routing structure and inside of here gives a page.tsx now inside of this page.tsx let's initialize that as a component we don't need any props for this so we can get rid of that it's just going to be a functional component no props needed and then here we are going to render a main or we could also render a section that works just fine with a class name of padding top 8 and then in here are going to be two things one is going to be an H actually Let's do an H1 with add a friend to let the users know what this page is for and then secondly actually let's give this a class name beforehand or font Dash bold text Dash 5xl and a margin bottom of eight just to separate this a bit from the component that we are about to implement which is going to be the add friend button now that is going to be a client component we're going to um just do some separation of concerns here the page should not worry about implementation details in this case um so we're going to declare that as a separate component called add friend button that doesn't exist yet so let's create it under the components new file and then add friend button dot TSX also initialize that as a typescript component and inside of this component there's going to be some cool logic that I'm going to get used to to handle input user input right because the user can input the email that they want to add and my camera was about to die so I forgot my train of thought but I'm going to show you some cool um ways to handle user input anyways so this is going to be a client component because we're going to handle functionality use client that's how we declare this as a client-side running component in react that would be the default right react is only client-side rendered so every component would be used client not in xjs and so we have to declare that separately and inside of this component we are gonna have a form we turn form we don't need the action for now because the action is going to be handled first later by a library that is specifically for handling forms super convenient the form is going to have a class name of Max with of small and that's it then inside of the form is going to be a label for the email component the email that we're going to input that lets us Define who we want to add with a class name of block text Dash small font Dash medium that is the font to weight so medium would be 500 the equivalent to CSS a leading of six leading six is the line height we want to specify that and then a text Dash gray Dash 900. okay and this label is going to say add friend by email just to be very clear to the user what they are about to do and then below that label is going to live a div the div is going to get a class name of margin top of two to give it a bit of offset flex and a gap of 4 between the top level HTML elements that are going to be in here those are going to be two first one is going to be an input of type text and the input is going to get a class name of block the class name is going to be a bit longer because the input is a very important piece of this component and with a full around it Dash medium that's going to be the Border radius a border of zero a padding y of 1.5 a text Dash gray Dash 900 a shadow of small then we want to give it a ring if we are focused on it ring 1 a ring Dash inset and I'm not too sure what that does honestly and then a ring of gray 300 a place placeholder of text Dash gray Dash 400 when we are focused on the input element we want to highlight it so a focus of ring Dash 2. also when we are focused the ring should be inside again I'm not 100 sure what these insets do and then when we are focused we want the ring to be of color indigo 600 that's a brand color we're going to use across the whole application from small devices and up this is mobile first by the way we're gonna have a text of small and then when we're on small devices and up we're going to have a leading of six and didn't we already have nose in place no it doesn't seem like it okay great so we're mocking the Styles like up here so they look um uniform and then the placeholder actually let's go into the new line for that the placeholder of this input is going to be U at X example Dot com and that is our input done below that we're going to have a button and no we don't want a regular button this should be our custom UI button saying add it's going to be default button no static needed we've already done that in the button component and then if there is an actually let's just save that right let's just save it let's import the add friend button into our page and let's take a look at what that looks like let's go to slash dashboard slash add that's where we have this page and take a look at this great so we have the add friend add friend by email you at example.com and we have the little add button now the input looks looks a bit off it has like a black border it looks kind of weird and the text margin inside of it is not quite right either and the way we fix that is by going into or Tailwind config right here and so the tailwind.config.js and we can install a Tailwind plugin that's just gonna make that look prettier especially the forms you can say npm install and the plugin is called add tail1css forms just go ahead and type the adult and when you're done we can hit enter with the add Tailwind CSS forms install that it was super fast it's a very very lightweight plugin and then to install the plugin we can require it down here in the plugin section in the Tailwind config let's save that and that is all we need to do now let's recheck that reload the page and obviously we also need to start the server backup after installing the dependency run yarn Dev or npm run Dev to do that then let's result the page and you're going to see that the form looks way better than it did before the spacings right the ugly border is gone great now to handle the logic of actually adding a user how do we do that because right now and why is this giving errors okay now they're gone now to the logic of actually adding using I have no idea why these are open we didn't even write those okay how do we add a user right that's the question so first off let's handle the logic for the input right what is a valid input and what isn't to do that we are going to install a library called npm install react Dash hook Dash form it's a super widespread library for handling forms it just makes forms that are originally a hassle in react way better and way more enjoyable to work with also we want the add hook form slash resolvers we're gonna see exactly what that does in a second and then we also want Zod to validate the user input and axios to make the fetch request and that's all we need to do those four dependencies react hook form hook form slash resolvers zot and axios go ahead and install those now since those are four dependencies that might take well that was really fast I was about to say that's going to take a bit but it doesn't and great okay so let's get started with the actual logic to do that let's define a function let's call it const add friend because that's what we're going to do it's going to be an async email and since we're adding Friends by email you also want to Resort [Music] um to accept that as a function parameter for argument wherever we call the function and in here we're going to handle the logic to add a friend as always we're going to have a try catch block if anything goes wrong during the fetching stage and the catch block can inform the user of water and wrong and first off we want to validate if the email that the user input is right if the user inputs something like you know one two three four five and click add they shouldn't even be able to make the request it just doesn't make sense and to do that we can say const validated email is going to be equal to and now the question is what is that going to be equal to and to have that validated let's go into our lib folder create a file or let's create a folder first called validations and in here let's create a file called add-friend.ts at the dash friend dot yes and then here we can import the from Zod now if you've never worked with the Zod what it allows us to do is Define schemas that are then validating the user input if that sounds cryptic you're gonna see exactly what that means so we're going to export a const and call that add friend validator because we're going to use that to validate the user input and that is going to be equal to a z dot object now essentially that's kind of like a JavaScript object but instead we can use it to parse something against it so the email that we are expecting from the user should be of type string right so every time we say Z we have access to like a Boolean or string or number depending on what we want this property to be and then zot offers us something really handy which is the additional email validator so what we can do is pass an object if I were to pass cons random is going to be equal to IDK of a string called ASD right if I were to parse this random object with this validator it would fail because this object does not have an email property that is of type string well that is of type string but it's not called email it's called IDK and it's not an email format either it's just ASD right it's not ASD asd.asd which would be email format so the parsing would fail and that parsing is super convenient when it comes to validating user input because here we can say add friend validator what we've just defined import that and then dot powers and we are going to parse the email that gets put in right so we can verify if the email that this function gets called with is a string and also of type email using a regular expression so if we have a validated email we know it contains a valid email that we can then pass to the server now on the server we're going to validate that again because anyone can post anything to the server but if the user is not actively tampering with our application that is at least not going to send a request in the first place and then to make the request we're going to use axios dot post make a post request to an API endpoint that we haven't created yet but that's going to live under slash API slash friends slash add that's what we're going to create together and then for the body content we're gonna pass the email that we want to add that is going to be the validated email object containing the email great so now we made the post request to the API wrote now we want to show to the user that it has worked right if we were successful with that response and to do that we are going to use something called state in react let's call it show success State and it is going to be of type Boolean as well and false I've already explained what this generic here does as I said you could leave it away and then you state we use um if we set that it updates the react UI and then is shown to the user so everything was successful right if this fails the catch block will get called and this code won't get called so if this code gets called right here we know it was successful we got a 200 response from this endpoint and then we can set the show success to something like true great in the catch block we want to handle the arrow and if like right now we don't know the type of the error it's unknown right so how would we display to the user that something went wrong we could display anything right we could just have a toast notification or we could validate the error so we could say if error is an instance off because arrow is a class right so we can check if a class is an instance of another class and from zot which we still need to import in here import the from Zod just like we did in the validation we get a super um super useful helper called Zod error which is also a class and then we can check is is the arrow that got thrown from our API wrote A Sort error and if it is then we should set that as an error somehow right that's also going to live in state actually let's do that differently let's let the library use to handle the form validation handle that for us we don't need to do a separate site for that we can just offset or offload that to the library that's going to handle the forms for us just keep that in mind for now for now we're just going to return if the arrow is a result arrow and then we're going to worry about the arrow handling here in a minute and if the error is an instance of the axials error which is also a class axials error and the automatic Imports aren't working again so let's reload the window that will usually fix it after re-initializing the JavaScript and typescript features now we can import that great if that isn't in stuff instance of an axious error then we are going to handle the arrow here in a second too and if it's not then we want something else to happen however we first need access to that set error function and we get that by using the form Library now if you've never worked with react talk form it's a library that works working with hooks in react way more enjoyable gives you a bunch of properties that you can use so for example those are going to be actually let's I'm going to show you what those are going to be first off we need to import the use form from react talk form this is where it all begins in this use form takes a function is a function that we can invoke and in here if we take a look at what we get access to we get access to a resolver property right here that we want to call now I know this is gonna be a bit harder to understand so I'm gonna pay close attention to make this as beginner friendly as possible to use the resolver we're gonna say those resolver that we get from the resolvers that we installed so we can say import zot resolver from and then add hook form slash resolvers great that's the Zod resolver and that doesn't work so let's try slash Zod and that works great so we had access to the zot resolver and then in here we're gonna pass the validator that we want to that we want to use right so we can say add friend validator so what we're doing here right we are telling the hook form if the value we're trying to pass for or oh okay and I need to restart the server you're in depth if the value we're trying to pass for input is not legit it's going to handle the error States for us so it's going to give us an errors object that we can handle that we can render on the page displaying to the user why exactly this action doesn't work so right now it wouldn't happen but that's what we're implementing right now and that's just easier with this Library similarly we can give this a generic type if you're following along in typescript of called form data that's going to allow us to register these fields turning them into controlled components for example this input how would we know what the user has written in here currently we don't and that's why we are going to pass the form data now this form data is I don't really know why it's already working I think it passes from JavaScript itself we want to overwrite that with a type of form data that is going to be equal to and what Zod allows us to do is this validator right here this is not a typescript type right but we can turn it into one and we do that by saying Z dot infer and then we can pass the type of add friend validator now if we take a look at the form data we can see it's of type email string how cool is that so it turned or validated in here that is in JavaScript into a typescript type that we are then using to validate or form input right down here and what the hook form allows us to do is get the register from the use form and now we can use that register maybe I'm a bit faster we can destructure that from the use form go down into our input right here and then call the dot dot dot so we're going to spread in the register which is going to give this input a bunch of props and if we take a look at this because we've given it the type and the validator we can see there is an email that we can register in this s and nothing else if I try to do something else it would give me an error that is the beauty of working with typescript and this validation Library called react hook form together with the zot resolver and salt okay now we don't only get the register we also get the handle submit that we can then use to work with the input data and the set error I was talking about so whenever something goes wrong and these in the API call we want to handle that error right and the way we do that is by calling the set error and then first off telling it where the arrow occurred and we can already see the email is in here how cool is that so it occurred for the email field and if we had multiple fields we could specify for which field or for all Fields um where we want to show this error and then the message is going to be error.message and we know this error has a message property because we validated it because we know it's of type assault error right and the same thing we're gonna do down here for the axios arrow so we're going to say set error and then also for the email and then axials the message is going to be a bit different it's going to be the the message that we're going to pass message is going to be the error.response dot data great that is optional typescript has done that for us super convenient and then lastly we're going to set the error if we couldn't pinpoint where the error exactly occurred it's not of type zot error it's not of type axios error then we're going to send the set the arrow for email as well with just a generic message of something went wrong and what we could also do is send a toast notification to the user and containing the error message for every key but since we're already displaying it this way there is no need to do it you could do it but there's no need to do it finally when the form is submitted we want something to happen right so currently if we take a look at this if we submit the form nothing happens yet the default action happens but that's not what we want so to implement the actual logic we can say const on supplement is going to be equal to and this is going to receive data of type form data beautiful because you already know what type it's going to be and then we're going to call the add friend with the data Dot and now we know there's an email on this data and to have this on submit actually be working the form gets an on submit by react now we can call the handle submit that we get from react hook form and pass that the on submit so that's gonna call this on submit function with that data whenever we submit the form and the button that we have in here is gonna be a submit by default because it's in the form however right now we're almost done with this component by the way we are not displaying the arrows anywhere to the user and that's not ideal so let's create a P tag down here with a class name of margin top of one text of small a text red of 5 or let's do 600 and in here we're going to display the errors that we get from react hook form we still need to import those let's go back up here and get the arrow Waits where are we getting those from okay we get them from the form set so we can say form State and from there we can get the arrows and worse there we go so we get the register handle submit set arrow and then the form State and destructure the errors from there and down here we can now call the errors dot we can see email might be an error and now we can call the Dot message oops start message on that email and that is optional and that's the arrow handling done last thing we're going to do is also display a success message right if the user was edit successfully we know that is the case because we're keeping that in state and if that is true we're going to render the exact same piece hack we can just copy and paste that over here enter here but change the text to Green 600 and instead of displaying error we could say friend request sent right and if the show success state is false then we are going to render out null no success message in that case let's try it out support wordful.ai click add and as you can see right now that didn't work that's literally just what we got back from the API world because that doesn't exist yet obviously this would be horrible to display to a user um but when the actual API Rod exists that is not going to be the case because we are going to handle the errors that come back to that let's create that API Rod I think it makes a lot of sense to do that at this point and we are going to get our feed wet and how to work with a database and how to handle the functionality so that's going to be super cool to create this API route first let's go into our source then the app directory and then enter the API now in this API is where we Define our custom API rods if you've never done full stack development an API Rod is server side and we can fetch information from the database send it back to the client to then just play it there inside of the API let's first get rid of this hello folder that's in there by default from nexjs we won't need that and let's create a folder called friends for all operations that have to do with friends like accepting a friend adding a friend and denying a friend first off is going to be adding a friend right so we create a folder called add that's going to be the API path it's going to be localhost slash API friends slash add then in here we are going to create a new file called route.ts now this name is mandatory it's enforced by next.js naming con it's not just convention you have to name it row.ts it's not going to be reflected in the URL structure so the API would be API friend slash add that's it and then inside of the around folder we want to handle the pulse request we're making from the client and the way we do that is by exporting an async function called the HTTP method that we want to handle if you wanted to handle a get request this would be get if we wanted to add a put that we would that would be put if we want to handle a post that is going to be post and this post gets a request of type request which is a standard web API that's why we don't even need to import that just like you know the error for example the error class inside of here we're going to initialize a try catch block because we're going to handle async logic interacting with the database and now let's get started in this route excluding real-time functionality for now that's going to be a bit much to do it all at once but let's get started developing this API endpoint so we can add a user to our friends list so let's say Khan's body is going to be equal to and then a weight rec.json if you've never worked with the new API rods this is how we get access to the body content of the post request in the new API rods and then the structure the email from that let's call it email to add because that's that's what it is it's the email we're going to add as a friend and now as I mentioned earlier we're going to revalidate the input never trust the client input right they could send anything along so we're going to validate the input again using the same validator which is super convenient we already know what data we are expecting and we're going to pass it the body dot email that we are sending along great now let's work with the database and first thing we need to figure out is who do we want to add right we have their email but we don't have their user ID and actually we add users by user ID but that would be super user unfriendly to always have the ideas that's why we're using email on the client now that we actually want to add the user we need to figure out what's their user ID and because sometimes next year s13 API would have some weird caching behavior I'm going to demonstrate to you how we can do that through the database rest API so we can call this const you know rest response just to be very um very verbose about what this is going to be and now we're going to make use of the fetch like that inside of this fetch we obviously need a URL to fetch and that is going to be now we're going to be working with the rest URL right remember the EnV the rest URL that we got that is what we're going to use so let's copy that name upstairs URL go back into our route.ts and that is the URL we're gonna make a fetch request took the process.env.apstash that is rest URL we are going to make a get request to that API rod and we want to get well let's take a look at it let's go into our database how do we now figure out the email of this user we can see it right here user colon email colon and then the email of the user that we want to add right so and here we can say slash get slash user colon email and then email to add basically the email of the person that the user that is calling this API rod wants to add we want to figure out their ID right now if we make this request it's not going to work we also need to pass some headers very very important upstage needs to know that we are authorized to make a query to this database and we do that in the authorization header let's call it authorization and that is going to be Bearer and then or process.env Dot and we can go back into the EnV and copy that the up stash redis rest token that is going to authorize us to make a query to our own database and after we have that in the headers that's also also to bypass the weird caching behavior that I mentioned earlier have a cache as no store meaning the data is never going to be stale because this is a get request it would usually fetch that which is definitely not what we want instead let's just tell nextges to be very specific about not storing the result and always delivering fresh data that's the fetch request done now we can get access to that data constata by saying await rest response dot Json and then we can cast that result into a type let's log that out take a look at the type this is going to be and let's just make a request to that endpoint I think the server should still be up and running yes it is let's go into here click add and now we call that API route let's go into here and we can see the type is result null because well obviously the user will try to add doesn't exist in the database yet but we can see that we have a result property and that is of type string if we get an ID back right that's going to be of type string that's how I know that and the cons ID to add is going to be equal to the data dot result that we get back this string property right here great now let's find out who is even making this request right and we don't want to send that along from the client that's super unsafe instead let's get the session server side and we can do that by saying concession is going to be equal to a weight get server session and then here past the auth options and we also need to import those great now if there is no session just finding this out doesn't mean there is one right this could be session or no we can say if there is no session then this request is invalid right so we can reject this request by saying return new response that is the syntax of the brand new Nexus 13 API rods return your response of an authorized and pass an object with a status of 401 unauthorized now what we didn't do yet is verifying that this person exists so as we saw earlier the result came back as null right so this doesn't have to be a string this could also be null and if the id2 add is not existent if there is no ID to add meaning the person that this user that is currently logged in is trying to add doesn't exist then we can return also a new response rejecting this API request and this response is going to say this person does not exist you silly goose why are you trying to add this person they don't exist with a status of 400 bad requests obviously that doesn't make sense to add a person that does not exist great and one last thing we want to check before this is a verified request is if the ID to add is equal to the user that is logged in right a user should not be able to add themselves that doesn't make sense either so in that case we can return a new response and that response is going to say you cannot add yourself as a friend you silly goose why are you trying to do that and with a status quote of 400. great if we have all those checks done valid request and we can just handle that request right great you know what before that we should check if the user is already added someone should not be able to add another user if they've already added them that doesn't make sense the way we can do that is as I mentioned earlier there sometimes is a caching Behavior that's quite weird to deal with so the way we can get around that is by creating a custom helper function to interact with our database where we know that the result is not going to be cash right because especially with these get requests by default and they are going to be cached in Nexus 13. I'm pretty sure that's a Nexus thing and not anything to do with the database and therefore we are going to create a helper function to interact with our database we're gonna do that in a separate folder let's call that helpers where we just create helper functions to help us throughout the entire application and call this for example redis.ts we are calling this red this is not DB because this is specific for redis first thing we want to do is import or variables right this is going to be a function to help us interact with the database so we need our credentials to be able to do that let's call the up stash red is rest URL it's going to be equal to process.env dot up stash underscore redis underscore rest URL and the second one I'm going to copy from the EnV because I can't be bothered to type that out second one is going to be auth token is going to be equal to the equal to the process.env dot and then what I just pasted from the EnV file great now we want to type out the commands that we are allowed to pass so we're going to say type command and then the commands we're going to use this for are going to be Z range then s is s is member sis member and to check if a value is a part of a list in redis then get and then also as members because if you don't do that it's going to be buggy at some points no if you're new to typescript I've already explained what the type does meaning a command can be one of these literals right then the function that's going to help us interact with our database is going to be export async function and let's call this fetch redis this fetch redis is going to take two things it's going to take the command of type commands or actually let's call this command I think that's a bit cleaner because it's just one and then secondly all the arguments that are going to be passed into it we're going to catch with this dot dot arcs and that is either going to be a string or a number array right that's how we Define an array in typescript these angled brackets right here and then within the body of the function we can handle the redis response now this looks complicated but the redis help is going to be super straightforward essentially we can kind of copy and paste this code right here the rest Response Code because that is essentially what we're going to be doing let's let's just copy that over that's not passive just yet let's get the URL for the command first let's call this command URL and this is going to construct the URL that we're going to make a fetch request to it's going to be a template string with these backticks and then the up stash red is rest URL as the base URL then slash command and then slash oops that needs to be slash args that is an array we're gonna join those together using a slash so that's how we make requests to the UPS rest API there's a separate documentation if you want to go further into that but this is the format in which you can make requests via the um the rest API endpoint and then for the response we can just paste the code response it's going to be equal to a weight fetch we're gonna fetch or command URL instead of this whole thing oops command URL there we go and what did I do oops command URL and in here because we've already named this we can just copy the auth token value that we want to pass in the authorization header telling up stash yes we are actually authorized to do this then very important to prevent the caching behavior is the cache of noise door and that's basically all we need to do let's do a quick validation if the response is not okay so if not response dot okay then we are going to throw a new error until the hour class we're going to give a string of error executing redis command colon and then let's change this into a template string actually so we can tell in the console exactly where the error card let's have a dollar sign and the curly braces in the template string to insert a dynamic value and then the response dot status text great we're almost done after that quick validation we're gonna have a constant data be the response Json so a weight response dot Json that conversion and then returning the data dot result to the client great and that is essentially the same thing as calling or DB that we have initialized for post requests in the db.ts right here it would be the same thing as calling this one just without the weird caching Behavior that's why we Define this helper function and now in our route.s we can check if the user is already added right so check if user is already added if you already added the user you can't add them again makes no sense so we can say const is already added it's going to be equal to and then fetch redis the helper function we've just created if we insert a string now we can see the command types that we can enter right these are the valid ones and we are going to use is member for this s is member s stands for you know a set that we have in redis it's an unstructured kind of array of data you can imagine you're gonna see exactly what it looks like when we are going to work with the chat messages by the way and also when we are done implementing the friends logic um you're going to see exactly what the sets are we are going to check that and then second is going to be the string of what we want to query in the database we already know the structure for upstash right it's user and then the ID and we are going to create a custom structure for this first let's insert that user colon ID to add and then we are going to call this that's what we are defining right now incoming underscore friend underscore requests that's what we're gonna query and that's also where we're going to store any incoming friend requests and we're going to pass the session dot user dot i d and we need to pass that in this function session Dot user.id so we are checking is the user that is currently logged in a member of the incoming friend request of the person that we're trying to add and if they are that doesn't make sense we're not going to allow that if is already added shouldn't work so you know the drill by now returning a new response saying something along the lines of already added this user with a status of 400 bad request that does not make sense and this needs to be typed out s0 or 1 because either the user is a member or they are not and we also need to await this because it is an asynchronous operation great now we need to check one more thing and yeah we want to make sure this is really secure and if they are already friends right you shouldn't be able to add someone who is already your friend and we can check that similar right we can't honestly just copy and paste this I did that by marking it and then pressing shift alt and down arrow we can call this is already friends and this is already friends it's gonna be the SS member as well but instead of incoming friend requests we're gonna check the friends the idea to add is gonna go last one I need to add that's what we're gonna pass at the arguments and the session dot user dot ID so we are checking in the friends list of the current user that is logged in that's the ID to add already exist we could check the other way around it doesn't really matter because if one is the friend of another one then another one is defend of one right it's always a you know when I think it's clear what I mean right two people are always when you are my friend I am your friend right that's how it works it can't be one-sided that's what we're doing here great and now we can finally after doing all that validation send a friend request send friend request there we go and we do that by saying DB Dot and in here we can actually use the DB there's no not going to be any weird caching Behavior because um only get requests are cached by default and not pulse or put requests that we're doing right now so the way we do that is by saying s add the database is sad no it really just means um we're adding to the set and what are we gonna add we're gonna add the user of type ID to add now that's where we're going to add it the user ID Twitter and then for that person the incoming underscore friend underscore requests so we're going to insert some data into that list and what are we going to insert we're going to insert the session.user.id so the user that is logged in is going to be put into the list of the incoming friend requests of the user they are trying to add and if all that has been successful we can return a new response saying something like okay and by default the status is going to be 200. if there is an error we can do some error handling if you already um you already know the drill right with a zot error if the error is in instance of a z dot zot error we still need to import zot to do that check so let's import it at the top import Z Z from Zod because right here we are parsing and if this parse fails if there is not an email incorrect format being passed to this API Rod then a Zod arrow is going to be thrown that's why we're checking this down here instance of Z dot dot error and okay we didn't handle that error correctly let's quickly do that um is already friends yeah I just copy pasted that already friends with this user remember to also check that right okay and after we've handled that we can make the request as I said we are going to check the error and if this is a zot error meaning if the email did not get passed correctly we are going to return a new response saying invalid request payload and the status is gonna be 422 for unprocessable entity pass to this API rod and if we can't know what the error is if it's just some random error that we can't point out where it happened we're just going to say invalid request so very generic error message with a status of 400. great let's see if that works so technically if everything is right we should get back either a user doesn't exist because we are the only user right now and if there are multiple users then we will be able to add another user so let's try this this person does not exist great because they've never signed up for our application let's do that however let's go into localhost 3000 slash login let's login with the other account that's going to automatically create the user data that we can then use to add this friend so right now I think I'm logged in with the top account let's log in with the bottom one and then that's going to create the user data in the data base why is it not doing that name did I log in with the wrong one oh I think I'm already logged in that might be it so I'm going to log in from a different browser the original Chrome browser let me go to localhost 3000 here my other screen log in with my just with a random other account okay so I'm going to log in with my second account it's the support account right here and I had an issue that I cut out because it's not an issue anymore you won't have that issue and it's fixed however let's continue where we left off so I logged in with my second account and now we want to test if we can actually add a friend so let's try it out add friend and let's go ahead and add our admin at word full dot AI account press add and even though that person should exist in the database those were some debugging logs that you just saw even though our account does exist in a database there is one problem and that is the weird caching Behavior I talked about earlier because I just did some debugging that I'm probably not going to include the data that we get back right here for the rest response is null even though that user clearly exists in the database and we can verify that even so I still have a debugging log in here it says get ID and then email to add right so it says get ID admin at word for that dot Ai and technically we should get the ID for that user back from our query and we can verify that we should get it back if we go in here and say get user colon email colon and I'm going to zoom in so you can see this better as you can see I've already done that query admin at wordful.ai and we can verify that we should get an ID back in the CLI so the ID exists in our database however due to some weird nexjs caching Behavior this rest response doesn't Deliver Us with that data so instead what we're gonna do is a const I did to add and now we're going to use the fetch redis Helper because that fetch redis helper gets rid of the problem fetch redis that's why we did it and we are gonna have a command called get and what we are gonna get is the user colon email colon and then as the dynamic parameter the ID so add right here as string and that was email to add and not added to it as string there we go and there's a naming conflict let's get rid of that line and there's still one issue because we probably need to avoid that operation okay now we are awaiting the ID to add and now let's see if this works I hope it does let's go to admin.wareford.ai click add finally great some debugging later friend request send great good work it's funny how even though I developed the project out completely stuff can still go wrong when you're filming the video for YouTube anyways that's just how it works different podcast has been sent let's verify that in our data browser so right now there should be a friend request friend request for this user great it's in our database for this user right here the C7 ending in DF incoming friend requests and we have the other user ID that is mapped to our support email the 8106 8106 that is the support at workflow.ai as the friend request so we successfully sent a friend request really really good great now the question is how do we display the friend request because a user should be able to see which friend requests they have right and the way we do that is through a layout component and the beautiful thing is if we display a layout the layout is going to be the sidebar on the left as you saw in the um in the demo of the application that's the layout if we Define a layout in next year s13 even though we are switching the pages the layout State doesn't re-render which is good for performance obviously unless we wanted to we can always force it to re-render and that's what we're going to do next we're going to do the sidebar in which we see the option of the friend requests or what we could also do is not worry about that for no actually we're gonna have to do that anyway so we might as well do it now we could also just make the page itself with a friend request but we gotta do the side part anyways so we might as well do that now it's gonna look great and be a big help for the rest of our application so for the sidebar let's create a layout under the dashboard folder and that is going to be a layout for all pages that are in the dashboard directory so for the ad the main page.tsx this layout is going to be for all of them layout.tsx now I'm going to link you a second snippet it's pretty convenient it's kind of like the FC just an LC for a layout component as you can see that's pretty cool it gives us a layer layout component if you can't be bothered you can just type it out it takes 10 seconds or component takes a children prop all the pages that are rendered through the layout that we are then displaying somewhere on the page and children are of type react node if you're following long typescript if in JavaScript you don't even need to worry about that great and then in the layout what we can first do is get the session concession is going to be equal to a weight get server session now to be able to use the away let's pass this past this the auth options first to be able to use this await we can make this asynchronous and then we can remove this first part because that doesn't go well with the asynchronous part and that in then instead we can declare these children here in line or we could say lay out props the props we have to find above of type react node so now or typescript IDE knows or you know visual code or IDE and typescript no this is of type react node and if there is no session right this is an authenticated part of the application nobody should be able to access this part without being authenticated therefore if there is no session if this is null either this is a session object or it's no if there's no session we don't want any user to access this content therefore we are gonna mark this as not found now this is a kind of Last Resort this will not get called if you initialize middleware correctly which we're going to use to protect or sensitive routes obviously this is not what we're going to rely on however we still just want to keep it in there just in case you know and then we can get started with the layout I think it makes sense to get started with that no first off first main div is going to get a class name of width of full flex and a height of screen so we always always make sure it's 100 viewport units high we're going to worry about the responsive stuff at the end I don't think it's very crucial to do that right now let's get the functionality working first and then as the second div inside of this let me just make a bit of space here this diff is going to get a class name of as I said let's do the responsive stuff first so it's not going to be hidden for now let's mark it as Flex give it a height of full a width of full a maximum width of XS which means extra small we don't want the sidebar to be huge you know then this is going to get a grow property which is a flex property Flex grow of one a flex column Gap y of five vertical spacing between reflex elements and overflow y of Auto so if the height is too large then there's going to be a scroll bar a border right a border gray of 200 Dash 200 ABG of white and a padding X of six and that's quite a bunch of class names but I think that was one of the largest divs in the whole layout then in here we're going to have a link component that link is going to contain our logo which we don't have yet but we're going to worry about that in a second first off let's talk about where that link will point and because of our routing structure or home page is our dashboard page so that's where we want this link to go slash dashboard now I'm making this super beginner friendly so if you don't know what a link is it's basically an anchor tag that next.js provides us where the page doesn't hard reload when we click it when you click in Anchor tag everything else is reloaded the page is kind of refreshed with a link component from nexjs that is not the case resulting in the link being faster than a a tag and it being best practice to implement the link unless you specifically have a reason to use the original HTML anger tag which we will also have a reason for later in this video by the way okay this link is going to get a class name of flex height of 16 a not 126 height of 16 a shrink of zero it's the flex shrink property that we're applying here and then an items Dash set Center okay and in the links we want to display our logo right however we don't have a logo just yet now for the logo and for crucial icons of our application let's create a component that will hold all of those and that component is going to be called icons let's initialize that component let's close all tabs first and then go into components this is where the icons are going to live icons.tsx now this is not going to be your average react component there's going to be a small difference we can literally just export cons icons it's going to be equal to an object and that's it right so from here you can export the logo and this is going to take some props as Lucid props you see props is basically just a type and it only allows props that an icon can take so like class name height with whatever we can pass the logo everything that works just fine with an SVG because the logo is going to be in SVG format now to get that SVG again I don't want to type this out with you I don't think that makes sense at all so how about we go into the GitHub repository into the source directory and then under components you can find the icons in here we've got the SVG we can just copy that I'm going to move that over and we're going to pass the SVG right here as the logo if you have a real logo you want to add to this application go ahead and use that amazing and then we are gonna actually while we're here we can already export one more logo we're gonna use that later on that is the user plus and the Auto Imports aren't working again so we're gonna import that from Lucid react up here user plus that's just an icon we're gonna need later and then also for later while we're already here just so we don't have to worry about it we're gonna export the type of these icons and we can do that by saying export type icon it's going to be equal to key off type of icons now if you're wondering what that does is it turns or icons into either a logo literal or a user plus literal that's going to come in super handy later when we're passing one of these icons as a prop because technically this is a function and you can't pass a function as a prop between client and server components what exactly I mean by that you're going to see later um we're done on this file we just export the type we get the user plus icon and the logo icon so that we can display the logo right here in or in or layout component the logo is going to get a class name of height of 8 width of Auto and a text Dash indigo-600 and it's going to be a self-closing tag right here great so if we take a look at what we've done so far if we reload the page and the server is that still running server is still running we can see there is a sidebar here on the left hand side now the content has been pushed to the right now the logo still looks a bit off um we're going to work on that but the sidebar is already there that's what we're currently building okay let's move back into our code and inside of these inside of the sidebar we are gonna render out something called your chats which is going to contain all the chats that this person has we're gonna have a div in here with a class name of text Dash XS in front of semi-bolt leading of six and a text Dash gray Dash 400 and here we're going to say your chats let's take a look at what this looks like refresh the page and it seems like it's kind of glitching out oh and I think I know what the errors because we put the first div here as a self-closing div and that's not ideal obviously we want these children to be contained in that div if we go ahead and save that then that is going to be put into the sidebar however the children should not be rendered out like that instead we're gonna position the children down here so they're not included in the sidebar but rather right of it so let's reload the page and now we separated the sidebar on the left hand side from the children great okay that was just a visual bug that I wanted to address and now we can keep on working on this sidebar great let's go back in here and then under the your chats section we are going to render out the chats that this user currently has and we're gonna do that in a nav element with a class name of flex flex-1 and flex Dash column now if you're not too familiar with CSS here's a little refresher Flex 1 means that inside of a flex it should take up as much space as possible without taking anything away from the other children if they really need it but it's going to fill up the remaining space then Flex column means that we are listing items beneath one another inside of this nav element we're going to have an unordered list with a role of a list and a class name oops class name up there there it is calcium of flex flex-1 Flex Dash call and a gap y of seven just to get some nice spacing between the different chats that this user has and inside of here we're going to render out a list element and now we would need access to the chats that a user has for now chats that this user has let's just leave it as this comment let's take a look at what this looks like in the browser there the chats would go right so this already looks good but we don't have the chats just yet we're gonna do that afterwards and then there should be an overview of the actions that the user can take so add a friend and see their friend requests right and we are going to render those out as another list element right below the other one and this element and inside of here we're going to have a div with a class name of text Dash XS a font of semi bold leading dash six and a text-grade-400 saying overview so this is going to be the same as your chats it's going to be the same style let's take a look at this it's going to be the overview and I would actually put this in a side by side but I think me being zoomed in this much um would clip the code on one side that's why I'm not going to do it let's stay in full screen view for now and then below this we won't have another unordered list containing all the um actions that the user can take right um so this usual also gets a row of equal to oops equal to a list and then a class name of minus margin x minus two minus 2 I just said that in German a margin top of Two and a space y of one just to separate the list items inside of this UL by just a bit now if we wanted to render out the options a user has we have to know which options do they have right so to do that and make it easily expandable let's declare that as a constant at the top of the page right here on the layout page let's call it site bar options and also let's declare a separate type for that just so we don't make any mistakes in the future this is going to be of type sidebar option array and we can just declare that type inline right here so not in a separate file because it's not going to be reused across the application it's very specific to this implementation right here so let's create an interface sidebar option as I explained earlier that is to not make type mistakes and get type safety when writing out the constant because a constant by itself could take anything right how would JavaScript know what is wrong and what is right in a constant that is totally up to us that's where we're declaring this type separately that gets an ID of numbers so each cyber option will have an ID a name of type string then it will also get an href of type string what should happen when a user clicks this element then an icon in capital of Icon because this is going to represent a react component that we're going to render um so I like putting it in uppercase and then a current we could add that if you wanted to display some additional what's the current site logic I'm going to leave it out for now and just to leave some overhead away now the icon type is coming from components and then icons that's what we declared right here that's this icon type we exported so we know it's either a logo or a user plus it can't be anything else forcing us to add the icon to these icons if we want to display them inside of the layout so let's import that icon type and that's going to be it four or sidebar options no we can get started creating the actual um the actual array that's what we need with an option with an object inside right and now we get beautiful intellisense on what we can pass for example the ID is going to be one for a first option the name is gonna be add friends then the href is going to be the route we have already defined for this slash dashboard slash add and then the icon now we also get beautiful intellisense as to which icons exist either a logo or the user plus let's go ahead and select the user plus and as I mentioned earlier instead of passing the icon function directly because we can't this is a very nice workaround to get around that so now we can pass the icon to the um as props pretty much and now we have a list of options that we can map through if you wanted to add to them at any point in the future you could just do it up there and then declare the page which is super nicely extensible so to display them we're going to say sidebar options down here in the UL that we have created together options dot map and for each sidebar option let's just call it option we are going to render out a function for now so a code block that returns some jsx later however we are using a function block because we need to calculate one thing and that is the icon that we want to render so let's say const icon is going to be equal to icons that is the component that we need to import and that we've created together which contains the logo and then the user plus at the index of option Dot icon and since typescript knows what this can be either user plus or logo it can know that the icon will always exist as for the jsx let's return a list element we're in an unordered list so the next logical thing is an Li element with a key of option dot ID and then inside of this Li element we're going to render out a link coming from next.js this link as the a tag also does gets an href and this href is going to be the option dot href that we've configured also this link is going to get a class name let's give it a class name move this to the next line and the class name is going to be text Dash gray-700 if we hover over this hover we can use this pseudo class from Tailwind CSS a text Dash indigo-600 if we hover over this then we want the background to change so BG Dash gray Dash 50. this is going to be a group we can just initially initialize that with a group keyword allowing us for children to know if this group for example is being hovered then we want it to be Flex have a gap of three be rounded medium so we're giving it a bit of Border radius we want this link to have a padding of two a small text so text Dash small leading oops leading dash six and a font of semi-bolt that's all the class names this link is gonna get that's enough for now and then inside of this link we are gonna have a span now why is this there we go There's the link inside of here we're going to render out a span element now this span is going to be responsible for the actual icon that is displayed this needs to be look beautiful as well that's why this also has some class names text Dash gray Dash 400 for example then a border Dash gray-200 a group Dash hover that's why we use the group previously so if we're anywhere in the link component this hover will be true the group hover with a border Dash indigo-600 and then also when we are hovering the group so group Dash hover again we want the text to be Dash Indigo Dash 600 S1 and there shouldn't be a space there and we want this to be flux have a height of 6 a width of six because this is going to render the icon we can do that a shrink of zero this shouldn't become smaller than it is items Dash Center justify Dash Center for this is for vertical alignment this is for horizontal alignment and then a rounded Dash large we want the icon container to be have a lot of Border radius to look better a border a text off and then to insert a custom value for the text because we're not going to reuse that across the application this is just a one time one-off um thing that we're gonna do we can put this in these angled brackets and then declare a 0.625 Ram text for that a font Dash medium and lastly ABG of white trust me that's that's all the class names for the span that's it and now styling wise we're almost done with the icon styling inside of this span let's get the closing element inside of this we're gonna put the icon that we have calculated up here that we get from the icons component we're going to put that right here with a class name height of 4 width of four and that is going to be self-closing and then right below the span we want to have another span that is going to contain the option dot name so you know add a friend for example and we're going to add a class name so this span which is going to contain one word that is truncate truncate applies a bunch of CSS cells meaning that if we were to shrink the window the text wouldn't move to the next line but it would be cut off by dots so for example add a friend dot a DOT right if there was no space let's take a look at what we've done so far these are the options that we've implemented and now if you wanted to implement another beautiful option all you'd have to do is go into your component and add it up here and would automatically be displayed in that line for you that's why we did that whole integration add friend and that should obviously lead to ads we're already here if we go to the dashboard okay this still looks a bit weird but we are going to work on fixing that don't worry about it everyone the ad route this looks just fine great good work until now and let's plow right ahead with something like the profile information at the bottom right that's also crucial to implement and to do that we're going to stay in the layout we can get rid of all the other tabs we won't need them right now and then let's declare another Li element right below this one so we have the LI we're declaring right now then a closing UL tag a closing nav tag closing div tag and then the children that's where we currently are and this Li tag is going to get a class name of minus m x minus 6. Mt Dash Auto so what we're doing now is building the account information right the profile picture the name and the email and the sign out button that's at the bottom of the sidebar that's what we're currently building this is going to also get a flex class name and then items Center and in here we're going to put a div with a class name of flex Flex dash one items Dash Center a gap X of four so this Gap is only going to be on the x-axis a padding X of 6 padding y of three text Dash small a font Dash semi bold and for the name later on for the account name a leading dash six and a text Dash gray Dash 900. great let's open up that div and take a look at what we are going to put inside this is going to contain the image the user image so to render that um we have to we're going to have to declare one more div with a class name of relative that is very important because by default next year has images are absolute so they will adhere to the closest relative element that's why we need to declare this is relative with a height of 8 width of 8. and a background gray of 50 and now since we've declared that as a relative we can put the image in here it's going to be self-closing if you don't know if you're new to next.js this image is basically like a regular HTML image but we need to declare the relative element above it as I just mentioned and it's automatically optimized it's one of the best features of Nexus it's super cool it automatically optimizes the image for us no matter what we put in there it's going to be performant super cool this image is going to get the fill property a referrer policy of no dash referrer and we do that because when we get the images from Google um sometimes it doesn't show them if we don't set this referred policy a class name of rounded Dash full to make this um a circle a source of option Dot and no that shouldn't be option that should be session dot user dot image and since that could be undefined we're going to do an or an empty string if there's some kind of error then we're not going to display an image there and then Alt is going to be your profile picture just for some accessibility great below that div containing the image we're going to initialize a span called your profile for accessibility purposes as well so we're going to give it a class name of Sr Dash only now what does this Sr only do essentially it's a bunch of CSS properties hiding this element from any person so nobody's going to be able to see this text but screen readers are so visually impaired people for example are going to have an easier time navigating our page this way then below that we're going to have a div with a class name of flex and flex Dash call to sort these elements below one another with one being the span element and in here we're going to write the session dot user Dot name that we get from the Google login that span is going to receive a class name that no that's not going to receive a class name it's going to receive an area Dash hidden of true and so meaning if visually impaired people navigate this website we are not going to show them their own name that doesn't make too much sense and then in here we're gonna put a class name of text Dash XS a text Dash zinc Dash 400 which is very close to gray but I think looks just a bit cleaner in this case and also an area hidden area Dash hidden of true just to give impaired people a bit of an easier time and then in here we're going to put the session dot user dot dot email right let's save that see what we did so far refresh the page and we still need to add this as our next JS domain as an allowed domain where we get images from in next.js you can't just get images from anywhere because that could potentially be malicious right if somebody manages to inject some image into your Source right here that could potentially be very bad for you so we have to allow images in our next JS config we can go under the next config and then that should be domains right why is this giving me an error pretty sure oh no this is images right images and then in the images that's an object that takes a domains array and then here we can whitelist the domains we allow images from in our case that's going to be lh3.googleusercontent.com if you support other sign-in methods they would also go here that's crucial and because we changed a nexjs config variable we have to restart our server just like with an environment variable for example in both cases we would have to restart so let's do that and go back to our application and see the results so great now down here the account details are being displayed we get the name we get the email now the only thing missing is the sign out button that we are rendering down there which is going to be super straightforward let's go into our component do that really quick let's go right under the div right here right above the Li and then render a sign out button with oops with a class name of H dash full and aspect Dash Square now the sign sign out button doesn't exist yet so let's create it under the components and let's call it sign out button dot TSX the reason we can't have the button right in the layout is because this is going to be a client component because obviously when the user clicks the button something should happen and we can't do that in a server component that's the reason we are putting this in a separate component this is going to get a state of is signing out so we know if the user is currently signing out of type Boolean and by default this is going to be false and I've already explained what this does so the rest is just regular react state and then we are render rendering out or reusable button this is where it comes in clutch again and we are receiving props in here right we remember we passed the class name for example how do we receive that we can extend this interface we can say extends button HTML attributes then HTML button element I've explained what this does when we build out the button basically we're saying this class should have everything that this class also has which is all the default stuff that we can pass an HTML button meaning we can just spread in all the props beautifully in here this button is going to be of variant ghost so it doesn't stick out too much and on click we're just going to Define an inline function asynchronous and the following code is going to happen your set is signing out so true then after that it's going to come a try catch block and the try we're gonna await the sign out method that we get from next auth react super convenient and if we catch the error we're gonna say toast dot error and then say something like um there was a problem signing out and then also the set signing or we're going to put that in the finally finally no matter what happens we're gonna set is signing out to false okay and in the button we are going to say if the user is signing oops is PSI is signing out then we are going to display a loader oops Loader 2 icon from Lucid react with a class name of anime oops of animate Dash spin a height of 4 and a width of 4 and when we are not currently signing out so in the other case we're gonna display a log out icon so it's apparent to the user what this button is doing and we get that icon from Lucid react or trusted icon library with a class name of width of 4 height of 4. and that's it great so now we only need to import that sign out button there we go save that and let's take a look at this let's render this read out the page and now we can see a beautiful sign out button right down here next to the name and the email that we can press and it's going to sign us out great and because we're not because we're not authenticated the dashboard will return a 404 um which is what I talked about earlier we are gonna very validate this through middleware of course so this shouldn't happen but even if we set up the middleware incorrectly somehow then unauthenticated users are still not able to access the dashboard just as an extra security measure just in case it doesn't hurt you know great oh by the way this is the only thing I did without you I have implemented the sign out button this is just for debugging purposes this has no effect on the project whatsoever um but I just want to make sure you're following along every step of the way so I'm going to tell you about this I just have a button here on the homepage saying sign out that's all I did and you don't need to copy that it was just for debugging but just so you're not thinking I'm doing stuff here without you great so far we have completely implemented the authentication step which is amazing that is a central part of this application we're displaying the user data on the front end and now we can also add friends and that is being shown in the database let's go ahead into the database and quickly log in with Google I'm going to use this account and then as we saw no we don't want that get rid of that as we saw the friend request was somewhere in here there we go as incoming friend requests with the other email great now we want to accept friends right we don't just want to add them we also want to accept them and that is what we will do next so first off we need an option inside the layout that lets us navigate to the friend requests page right that will be under the add friend and we will do that inside of the dashboard layout so let's navigate in here and that link to the friend requests will go right here in between the other two Li elements that we have in here so underneath is the your profile section that contains all the information down here and then above is the your your chat section in the overview and we're doing it right in between those two right here between the two Li's and let's initialize nuli to do that after all we should we are still in the list and then here we will render out the friend requests as a client component because later on we will be implementing real-time functionality and for that it's crucial that this is a client component let's call this the friend requests sidebar option I know the name is quite long but it does describe really well what this um component is going to be and inside that friends request sidebar option we are gonna go so let's exit out of our app directory and create a new component called friend request site bar options dot TSX great let's initialize this as a new functional component and The Styling is going to be pretty simple for this one so what we're gonna do is first off we want to create a link right just like the add friend is a link if we click at the main page here changes the same thing is going to happen with the friend requests so the underlying behavior that we want for this component is going to be a linking Behavior and we do that with the next link this link is going to have an href and no that's too much zoomed in I think I was like this for me it's a new day so maybe some settings changed but I hope the zooming in level is still the same um the href behavior is going to be slash and dashboard slash requests like that and then we're gonna worry about the on click Handler later and this link is going to get a class name let's put it after the href so it's a bit easier to see the href that class name is gonna be text Dash gray-700 when we hover we're gonna put the text to Indigo 6 oops 600 then also when we're hovering the background will become gray of 50. this is going to be a group element to track behavior that um you know that is when we hover over this link element marked as the group then we can listen to that behavior in all children elements and style them accordingly that's what it's for this can be Flex items Dash Center for vertical alignment and we want a gap X of 3. rounded of medium to give this a bit of Border radius a padding of two we are not differentiating between X and Y padding here just a general padding of two a oops a text of small a leading of six and a font Dash semi bold so the sliding is gonna equal the other option that we have and that's it for the link in here we're gonna put a div with a class name of text Dash gray Dash 400 a border Dash gray-200 and now we can listen to the group event and we do that I think I showed it earlier um it's by group Dash hover and now we are listening in the child to a hover element to the link element because we marked that as a group right here that's what we're listening to and whenever we hover this link element the border is going to be Indigo 600 and also when we do group hover the text is going to be in the go 600 as well great we're almost done with the class names um actually oh no there are quite a few more and but I promise this is the this is the longest div um there there are a couple class names let's just uh plow ahead it's gonna be Flex height of six width of six a shrink of zero to not make this go any smaller than it is an items Dash Center for vertical alignment it just justify the ash center for horizontal alignment around it Dash large so we're starting the the icon right now that's what this is for um a border a text of and now in this curly in these angled brackets because we're inserting a custom value it's going to be 0.625 Ram we are not going to declare that in the Tailwind config as a special value because we're just using this one off a font Dash medium and then also fpg of white and I promise that with it that was it for this div and that was by far the longest class name in this whole component the rest is going to be super simple um so in here as I said we are rendering out the icon and that is going to be a user icon that we get from Lucid react just import it up here and this user icon gets a class name of height 4 and with 4. great so that's the icon done let's quickly save that and import this component and the Auto Imports don't seem to be working again so let's restart the window and then hopefully that will fix the Auto Imports give it a second to reload and did I mess something up print request sidebar options oh yeah I did sidebar option okay so that was the reason there we go now we can import that component and is still giving us an error because it didn't actually import the component great let's import that manually um from and then that should be at slash components slash and then we called it friend request sidebar options let's import that manually if vs code won't let us and let's take a look at what this currently looks like so right now this is the icon here below the add friend what we rendered doesn't look quite as um similar to the add friend yet that's because we haven't added the text yet so let's do that and let's quickly close these Pages we don't need them and then in below the div where we run it out the icon let's put a P with a class name of truncate meaning if the page goes too small it will be cut off by dot dot friend requests will be in the div in the in the P tag and that's gonna be it for now let's save that and that still looks a bit off I might have messed up the sliding somewhere um but that's fine for now let's just um keep plowing ahead and then if there are any minor style differences I'm gonna make a quick cut and let you know what those are and but for now that's fine let's implement the actual functionality so we want to see the friend requests that a user has so we should probably pass them in as the initial friend requests and then because we want to add to that later on um depending on Real Time Changes we are going to save those in States so we can change them later on and the way we do that is first decline declaring this is a client component by saying use client at the very top of the file to mark this as a client component and then initializing a new state inside of this component we're going to call this state unseen a request count because we don't care about the actual requests we just want to know the count to show that to the user and then that is going to default to the initial unseen request count um and that doesn't exist yet because we're gonna pass it from the parent so the way this will work is we're going to fetch from the database in the parent component because that is a server component right um we are rendering this inside of a layout which is a server component and then passing the the standard default value in here and during runtime when the users inside of the application we can still add to that with real-time updates which we're going to implement later um first off we need the initial unseen request count from the parent as a number and then we also want to import that in the actual component and then secondly what we also need is going to be the session ID we won't need that right now but it's going to be very important later on for the real-time functionality it's a very easy thing we can just import the session ID and or pass password as props I should rather say not import it and then we can also get it from the props as I said we're not going to need that right now but just let's have it in there while we're already here and now we get an error from the layout component because we need to pass the session ID and the initial unseen request counts let's pass those session ID is gonna be the session dot user dot ID that was easy however we don't have the initial unseen request count yet so let's do that next that is going to be the Unseen request count let's name it that that we're gonna pass in as the initial request count I can exit out of that and now let's work on getting this value at the top of the component because we're in a server component we can interact with the database directly and the unseen request count const unseen request count is going to be equal to and then let's use our fetch redis Helper and for the command we are going to use as members because that's going to give us back a number we also want to await this because it is an asynchronous operation and then as for which value we want to retrieve from the database that's going to be user colon and then obviously we want to do this for the session.user.id so for whoever is currently logged in and as we know from the database if I open it up right here the incoming friend requests is what we're looking for so let's go back into our component then insert a colon after the session user ID and then say incoming underscore friend underscore requests with an S at the end and because we don't care about the actual um members we can just say um well we first we need to await that we can wrap that in parentheses and then say dot length now because um typescript doesn't know the return type of this this would give us the actual member objects each one in an array as a user array that's what we get back and we only care about the number of requests right so this is going to be a number we can save that and now we should have access to the amount of people inside of the friend requests page this page doesn't exist yet we are going to create that in a second for now let's render out how many unseen friend requests we have right here in the layout that's very important um so let's go back into the friend request sidebar options and then as one tiny tweak to this component under the friend requests let's show how many unseen there actually are and we do that by checking if there are even more than zero and if there are more than zero then we're gonna render a div and if there are not more than zero so else this is a ternary operator so if that is if so if this statement is true then this code block will be executed else that's why the colon is there and we're gonna render null we don't want anything to show up and if the request count is larger than zero we're gonna show a div with a class name of rounded Dash full a width of five a height of five a text of extra small Flex justify Dash center items Dash Center a text of white and a background of indigo Dash 600 and let's just put a 2 in here for testing purposes let's go back and we can see friend requests 2 are unseen obviously we don't want to hard code this value but instead this should be the Unseen request count great now we are never setting the state which is fine we can save that for now let's see what happens we have one unseen friend request which should be the case because we have you know one unseen friend request right here in the database so it's working as expected however we can't interact with that request yet because the actual page we use for the requests doesn't exist yet so let's create it and to do that we're going to go into our dashboard and then inside of the dashboard file right here we're going to create a new folder so creating a new you know URL path in that sense called requests and inside of that request folder we are gonna put a page.tsx and first off initialize that as a regular typescript component as we're already used to doing great okay this component is not going to get any props so we can just leave this whole stuff away for now we don't need to worry about that and then first thing we want to do is get the current session of the user concession is going to be equal to and then get server session and pass that the auth options you know the drill by now and if there is no session then we can just default to not found for now great we're making really good progress if you're following a lot good job we are making really good progress in this component in the page we want to know who made a friend request to us right that is super relevant that's why we even got the session so we want to find out the ideas of people who sent us a friend requests or who sent the current logged in user friend request right that's what we want to know and we can do that by saying cons incoming set oops sender IDs is going to be equal to and then a weight fetch redis we're going to use our helper function and for this to work we need to Mark the pages asynchronous and remove the type if you're in typescript and then from redis what do we want we want the IDS of the people who made a request to us meaning the IDS of people who are in or incoming friend requests these ideas right here are what we want to know and to to get those we can say fetch redis we want to fetch the S members so the members of a set and then as for the command we want to fetch the use oops user then or session or the the currently logged in user um session dot wait why is that not working because we need to await the get server session the session dot user dot ID and then the incoming underscore oops friend underscore requests and there we go and because this doesn't know what that is this is a string array because we're getting back a bunch of um IDs right let's format that and then for the we we all okay um so let me rephrase that we also want to know the email right for the people that request that and we don't want or the the idea to be shown on the page right when we navigate the friend requests we don't want there to be a list of IDs because the user can't do anything with that knowledge realistically so we want to display the emails right that's way more important and to get that data we are gonna make one more query to the database for every incoming sender ID to fetch that person's data and we do that by saying const incoming friend requests is equal to a weight promise dot all and what promise.all does for us it lets us await in an array of promises simultaneously so each incoming friend request will be fetched at the same time and not one after another making for way better performance we are gonna map over the incoming sender IDs map so for each incoming sender ID we are going to call an asynchronous function just an inline function we get the sender ID as the parameter and then we are going to execute the following code block the const sender is going to be equal to and then a weight oops a weight fetch red is and then as the commands we're going to use get and then as the um well well as the command we're gonna say get and then for as to what we want to get it's going to be user colon and then the sender ID that's what we want to retrieve from the database and that is going to be of type as in user if you're following along in typescript so now um or IDE knows okay the sender has properties like you know email ID image and name whatever we store in the database and we can return that as the sender ID and then the sender email is going to be sender Dot email so we will have easy access to the sender email for the incoming friend requests great very very good now we are done with the logic on this page and we can work um work on rendering this out to the user to do that we're gonna create a main for jsx for this component with a class name of padding top of 8. instead of this main we're going to create an H1 to let the user know what this page does and theoretically what we can do is go into our other page the add page and just copy what we had in here so we don't get any siding wrong let's just copy those four lines and paste them over here and or requests page so the starting matches right that's important and then this is going to change we are going to have a custom component in here but first we want to create a div with a class name of flex Dash call and a gap of four so we want to list certain elements beneath one another each one being a friend request and these front requests are going to be contained in their own client component because again we are going to be using real-time features in that component so it's crucial we Mark that as a client component that we are going to call friend requests like that that can be self-closing that's totally fine and now that is a component that doesn't exist yet so let's create it let's go back from our app file and into our components and create a friend requests dot TSX file this is going to be a functional component and then inside of the friend requests we are going to determine what to show to the current user after all we've already done the the work and now we just need to display this to the user now just like with the other page that we did um we're gonna mark this as a client component because we're going to use real-time features later on and we're gonna save the initial request in state again let's call these incoming um for oops I tapped into caps lock friend requests and we are going to pass the initial friend requests from the from the parent component which is the server component so inside of the state let's put incoming friend requests and there is a naming conflict here so let's rename the state to just friend requests and then set friend requests and by that we are bypassing the naming restriction and this is going to be of type incoming friend request array now this type does not exist yet so we are going to create it and we are going to create that that's going to be your first interaction with kind of the real-time features the actual ones we're going to implement later but we can already create a type for this and to do that we're going to navigate into our types directory and create one last file for our types and that is going to be called pusher.d.ts that's what we're going to use for real time communication data this interface is going to be very straightforward let's call this interface incoming friend request and inside of this interface there is going to be a sender ID of type string and then also a sender email of type either string or no or undefined great let's save that and now this type is known because we have done that in a DOT d.ts file we don't need to import it now the incoming friend requests don't exist yet and we know that we are going to pass them from the parent so the incoming friend requests are going to be of type incoming friend requests as you might be able to guess an array of those and one more thing we want is the session ID because we've already done the fetching on the parent we can just pass it in instead of getting it from the client side and this component it just makes more sense to just pass it along and then we want to accept both of these props in the actual component therefore the arrow in the state is going to be gone and now we can actually work on the logic of the jsx inside of this component this component is going to consist of a fragment in the top level because we're going to Nest um or because we're going to conditionally render and to do that we need a fragment at the top and we're going to conditionally render based on the amount of friend requests that this user has so if there are none we want to display something else then if there are any so we're going to do the check front requests dot length is equal to zero and if it is equal to zero then we're going to render out what's in here and else we're going to render out what's inside of these parentheses right so so what we want to render out when there is nothing when there is no friend request it's going to be a P tag saying nothing to show here now this is pretty simplistic it's very clear to the user what this means it's probably not the most pretty solution but adding some really Advanced CSS showing some cool things would have added a lot of overhead to this project if you'd like to tweak this um the the empty State feel free to do that and that's one of the best ways to learn just adding your own stuff to this project is a great idea and for now it probably makes sense to follow along and then do all that in the end however if you want feel free to always pause the video and insert your own stuff to make this even better then inside of the p-tech it's going to get a class name of text Dash small and text Dash zinc Dash 500 I just found that works well with the white background and then if there are friend requests friend requests then we're gonna map over them and display something for each request and what are we going to display for each request we're gonna display a div and because we're mapping over every request this div needs a key to be identifiable and that is going to be the request dot sender ID we can just use that great because one sender can only have one active friend request this diff is going to get a class name of flex then gap of 4 and items Dash Center inside of this diff we're going to render out a icon user plus to indicate this user has gotten a new friend request with a class name of text Dash black okay this is going to be a self-closing icon we don't need to do any more with that then below that we're going to have a P tag and inside of the speed tag we are going to display the request dot sender email this is user friendly because instead of showing the ID we show the email that we have fetched from the database for this user and we don't want to leave it unstyled this should be with a font of medium and a text of large great okay then two buttons and we're done let's initialize a button and if you're using emit in vs code you can type button times two hit enter and that's going to create two buttons for you pretty convenient inside of the first button we are going to render out a check icon we also get that from Lucid react and this icon is going to get a class name of font semi bolt a text of white a width of 3 4 or 3 quarters if you're in the US and then a height of also three quarters great now the button is still unstyled that is not ideal first of what we want to do to the button though is add an area label because we're just using an icon as the button and no text at all it would not be clear to visually impaired people using a screen reader what this button does and therefore we're adding an area Dash label and that label is going to be accept friend and this button is going to get a class name of width of 8. height of 8 a background of indigo Dash 600 when we're hovering this button it's gonna get a BG of indigo-700 it's going to be a grid Place Dash items Dash center it's the same thing as applying a flex Flex um no it would be the same as applying Flex then justify Center and item Center instead we can just say grid Place item Center that's going to Center the items in that container in this case the icon that we're rendering is going to be rounded Dash full then a transition transition property which is a convenient utility we get from Tailwind CSS to make animation easy and then lastly a hover Dash Shadow Dash medium a hover colon Shadow Dash medium great let's quickly take a look at that button and I think we first need to import the component in our page under the requests let's import this component and did the also important not work again no it did but okay we need to pass the incoming friend requests first let's do that by just copying the value that we got from the database up here the incoming friend requests pasting it in here and then secondly we demand the session ID which is going to be you probably know the drill by now the session dot user dot ID great and that's all this component needs we can hit save and let me switch my keyboard back and if we take a look at what this does in the browser let's navigate to friend requests and see what happens oh great we can see the user icon and the check mark now we can't see the email just yet so let's work on fixing that let's go back into our friend requests and then we want to display one more button it's already down here but instead of having to type out all the button by yourself again I just copy and paste this one by the way I just copied the code using shift alt and arrow down let's have a bit of space between these buttons and then the button area label is going to be deny friend and I switched to English keyboard again how do I keep doing that and the class names are going to be lightly different instead of indigo we're going to make this red of 700 but the rest is going to stay exactly the same and instead of the check we want to display the X great let's hit save and go back into our component it seems like the email is not being displayed correctly I wonder why that is um to quickly debug that let's try logging out the incoming friend request that we passed to this component let's go into our page.tsx and log out the incoming friend requests and also put them in string so we know what those are if this is a bigger issue which I don't think it is but then I would make a cut um okay so in here we can see the incoming friend requests the sender ID is defined by the sender email is undefined and that means this get request is doing something wrong let's see what that could be the incoming friend requests let's see what happens so we're going to the user and then this sender ID right here the eight one and ending in all six let's check what that does in the database let's go back in here and then see for user which one was it user eight one and the email doesn't exist because that user doesn't exist that is weird I know this seemed like a rough cut but there was an issue with the login system and I fixed it the error was a bit hard to find um but it is a small tweak we need to do because previously at least for me I don't know um if it did the same thing for you maybe it even worked for you um if you don't have the same caching behavior and but it was an issue due to the weird Nexus 13 caching Behavior so now it displays the login just as we want sidebar is still a bit messed up but that's just visually um that is not going to be a problem later on um so the problem is in this line right here um whenever I logged in the issue was whenever I logged in no matter with what account it would always show that I've logged in with the first account I've logged in with right so the admin at wordful.ai and the problem is in this line right here because it caches the account that we log in with and that is horrible we don't want that so instead of using the db.get we're completely gonna replace this with our custom fetch redis helper this helper is super convenient because it bypasses that caching Behavior as for the command we can actually use just what was there previously let's go to the next line and then say const DB user result is going to be equal to a weight Fetch redis and then here we want the get command to be executed because this is a DB dot get that we are replacing as for the command we can literally just copy and paste it and as for the result this is either gonna be a string adjacent string so as string or null if no user is found now we can get rid of the first line we can completely eliminate that remove the console log for now and if there is no DB user result that means this user has not existed in the database previously otherwise if there is a DB user result we remember this is a string adjacent string then we need to parse it so we can say cons DB a user is going to be equal to Json dot parse and then the DB user result as user and that bypasses the weird caching Behavior so if we were to go back into the application flush the database as you can see I've done a bit of debugging there if we refresh the page we are automatically logged out so if we navigate back to the login page log in with a first account the wordful AI admin 1 then go ahead and let me zoom out far so we can see the button right now then log out again and then log in with another account for example the support account it should work just as expected because we are not having the weird caching Behavior anymore great so just to summarize to not confuse you we replace the way we get the previous user in the Json web token callback in the auth file and then if there is a result for that it's a Json string and we're converting that to a user that's all there is to it okay great um where were we we can go back and now let's navigate to the requests page we can see at a friend nothing to show here and now we can actually show friend requests right so we can go to the add friend request and for example let's add our second account let's click on ADD and friend request was sent successfully so let's log out of this account right now and log in with the other account to see if we have gotten that friend request we send that to this account right here let's login and let's get rid of the session data on the home page for now it just obscures the vision and so let's quickly go into the dashboard page this one right here and just get rid of this and just say dashboard so because this looked horrible before there we go we can see we have gotten one new friend request now it's actually working that we're over the weird caching behavior let's click on here but it seems like it's still not displaying the email correctly so let's dive into why that could be the user however should exist in our database great that is the case so this shouldn't be a database issue let's debug that let's first close all tabs just to get a bit more structure in here and then let's go into our requests page Log out the incoming friend requests and we're already doing that and we can see the sender email is still undefined so how about we log out the sender sender and then center right here let's save that reload the page and then hopefully that should log out the sender let's go back and here sender is wordful AI support email support wordful.ai so this is working correctly however I suspect this is going to be right so this is a Json string that is why this is not working it's the same thing as in the DB user that we just did this comes back as the adjacent string and therefore the column to read the email property so we need to convert this instead of the user to a to a string and then const sender parsed is gonna be um the Json dot parse of the sender that we get back from the database as a user and then instead of the sender.email we're going to insert the sender parse.ml and then if we read out the page we have debug the issue successfully on the Fly um and now it works now it shows the actual email great really really good work now we want to either be able to add the friend or to deny the friend request that would be very important that's all we're doing on this page so if we click on the check mark that should add a friend and if we click on the X that showed denial friend let's work on implementing that logic it's going to be a lot of fun because for this we're going to implement two more API roads and you're going to learn how we're going to handle Security in those API rods and the functions in the component itself are going to be very straightforward let's get rid of that console log and then navigate back to the friend requests dot TSX component where all the magic for the friend requests happens this is where we can accept and deny a friend so let's create the functions to do that first one is going to be const accept friend this is going to be an asynchronous function because we're going to make an API call inside of there and the API call we're going to make is by saying a white axios and we're going to import access to make the request dot post we're going to make a post request to the end point of Slash API slash requests slash accept this API Rod or endpoint doesn't exist yet we're going to create it and then as for the data we want to pass we want to pass the ID of the person that we want to accept as a friend which is going to be the sender ID now the sender ID we don't have access to that yet and that comes as a parameter or argument no as a parameter instead of this function argument I think is where an argument is when you invoke the function what you pass and a parameter is what you accept in the function I think that's how it was so we accept that right here the sender ID as a string that's how we know the sender ID in here and then after we accepted that front we want to put it into State and also refresh the page first off let's put it into State set friend requests we get the actually yeah we are going to receive the previous rent requests and then we're going to say prev.filter and for each request that gets passed in here and we're gonna do a check if the request oops request dot sender ID is unequal to the sender ID there we go and then afterwards we're going to say router dot oops and we don't have the router yet we still need to import that and that is a hook that next.js gives us it's um just the router cons router is going to be equal to and then it's called use router however pay attention that you import this from next navigation and not next slash router that's important because next slash router doesn't work anymore and we want this router from next slash navigation that we can then use to say where is it refresh there we go so at the very bottom of the accept friend function we are refreshing the page and by doing this we are filtering everyone out um so for example if we had three friend requests and we are accepting one of them we want to take that person out of the state to only show the other two that's why we're filtering out every ID of what we just accepted right and then a very similar logic is going to happen for the deny friend so we can just Mark the whole function block hold shift alt and arrow down to copy and paste this and obviously we want to rename this to deny oops deny friend and the sender ID is going to stay pretty much most of the logic is going to stay except the end point instead of accept we're going to send a request to deny and then the rest is going to stay the same great so whenever we accept it or denied a friend we want that person to be taken out of this state right here now the API endpoints would always fail because they don't exist yet and that's the next thing that we're going to do implement the accept and denying friend endpoints because accepting and denying a friend are the basis for even being able to chat with someone right we are requiring someone to be friends with one another to be able to chat otherwise there would probably be a lot of spam right um that's what we're gonna do imagine WhatsApp where you could just Spam everyone that doesn't want to talk to you or Discord where you could just write everyone that wouldn't be amazing so let's create the API rods for that we're going to do that in the app folder and then under the API we're going to create a new one for the friends and that is going to be accept and that doesn't go into the ads but instead the friends right here so we have API friends and then either accept or add at the same level and then in the accept we are going to create a route.ts and inside of this route.ts we can then handle the logic to accept the front great so inside of this file we're going to say export const or actually this is going to be an async function post as I mentioned earlier in the video the HTTP methods are required um so if we made a post request this code right here would handle it because we named it post if we named it get it wouldn't handle a post request so the naming is important for this one and inside of here we're gonna put a try catch we're going to worry about the error handling later totally totally unimportant we don't need to do that for now and then to get access to the body content of the request by the way we can get access to the request right here as the parameter it's going to be of type request and to get access to the body content is fairly straightforward we can say Collins body is equal to the weight rec.json that's how we get the body request and then we want to validate if this ID is you know a string anybody could pass anything as the ID and we want to check if it's a valid input that the user is making so we can say const ID is and let's call it the idito add because that's what it is right it's the ID of the person that we want to add as a friend or accept could also work right um and that is going to be equal to a z dot object we've already worked with Zod before and in this case we're also going to use sort to validate the input that is coming into this API Rod so let's import zot from zot to be able to use it in this file and for the salt object this is going to be past the ID and the ID is going to be a string because we are oops a string because we are expecting a string as the input and then we can call the dot parse function of that and parse the request body in there that means if the parsing is successful we will get an ID to add as a string and in all other cases if it's a number pass or function or whatever the error and the catch block is going to get invoked because the parse is going to throw an error no not anyone can access can accept anyone as a friend right we need to do a lot of logic checks in this one if we didn't I could just make a post request to this endpoint directly and tell the endpoint that I want to accept you as a friend even though I've never sent a friend request for example to you that would be pretty bad in terms of security right we don't want that so we're gonna perform a series of checks to check if the request that is being made is actually valid and this person is allowed to add someone in this case you or me as a friend first off we need to get a session for that we need to know who is making the request to do that we're going to call the get server session path of the past of the earth options and you know the drill by now and if the user is not logged in if there is no session then they can't even go further they need to be logged in no way around it otherwise we're going to return a new response and that is going to say unauthorized with a status that we can pass as the second argument as an object the status of 401. so great and then if there is a session and we need to verify verify both users are not already friends so you can't add someone who you are already friends with doesn't make sense right so to find out if that is the case we can say const is already friends and that is going to be equal to a weight Fetch redis and then as for the command that we're going to use we're going to say s is member so is this person a member of a set in this case the friend set um and to check that that is the command and for which part of the database we want to query it's the user colon then the session dot user dot ID and then a structure that you haven't that I'm going to introduce to you now that you haven't known before as the dot thread and that should be S and colon friends so for the current user that is logged in we're looking into the friends document and verifying that the person that is making that they want to add that the ID to add is not in there already else the request is invalid and if they are is already friends if that is truthy then they cannot make the request doesn't matter how hard they try we're going to return a new response that says already you know friends or something along those lines with a status of 400 indicating a bad request great now we want to determine if the person has actually had an incoming friend request the only people that you should be allowed to add are people where you had an incoming friend request right otherwise um you could just add anyone except anyone without having them send a friend request that's horrible so we're gonna say cons has friend request is going to be equal to a weight fetch redis once again and we're gonna fetch the is member the S is member again but this time we're going to fetch that for the incoming friend requests that's the difference so we're gonna put a template string as the second argument and that in here it goes the user in the session dot user dot ID and then the incoming underscore friend underscore requests and by doing that we want to find out if the ID that we're trying to add is already in these incoming friend requests now to pass that we are going to insert it right here at the end as the start argument ID so add and then let's log it out for now and let's just move this into a new line and console log has friend request question mark and then let's insert the constant that we have just calculated just so you get a good feeling as to what we're doing here let's save that and make a request to the endpoint so if we wanted to accept this user the API endpoint would get called let's press enter or the accept mark look into our console and see what happens if we scroll down um nothing has happened console log has friend request so let's see what's happened to the request we can do that by going into the Chrome Dev tools under the network Tab and it seems like we're not even making the request probably because I forgot to link the button and the function yes I did so we're going to link the accept friend function to the actual button so on click it's going to be accept friend and we can't just put it like that we need to pass this in argument which is the sender ID and the sender ID is going to be equal to the request dot sender ID similarly if we wanted to deny a friend we're going to put an on click Handler and then call the deny friend oops deny friend function with the same sender ID okay let's save that go back into the browser and now the network request should work right we made the request but we are getting back a 404 note found for API slash request slash accept let's see if I messed up the routing structure API slash friends slash accept no that should work and the devil is in the detail I put requests here and this should be friends we are not accepting a request per se but we are accepting a person a friend therefore that should be reflected in the structure there we go let's save that and try making that request again let's go to the fetch h x the xhr so we only see the fetch requests the check mark and that didn't work we got a 404 again did I spell that wrong yeah that should be friends okay now it should work let's go back make the request one last time and now we won't get a 404 instead we'll get a 500 interesting so let's take a look at what that means go into this and then the response let's take a look at why that could be the case and in this fetch red is right here it doesn't seem like we are passing anything to check for who is member of this so we would probably on the ID to add in there as well and let's see if that now works now we still get back A500 um I think it would be best to log out the error for now console log error here at the bottom to see what the problem is let's press on the check mark again see if you get any response for this we probably don't because the catch block is being invoked and actually after logging out the request one in the request 2 they have both gone through successfully and we even get a value for has friend request as one which is just as it should be so let's return a new response of okay and let's see if that is fixing the problem and it is so if we don't return a response we get back a 500 error which makes sense but I didn't think about that before that's interesting and so we just get back in OK response and now we know that the user is either in their friend requests or they are not and if they are not in the friend requests um then they can't add them right if not has friend request then they shouldn't be allowed to add this user as a friend because a friend request prior to accepting someone as a friend is mandatory so in that case we're going to return a new response like no friend request with a oops with a status that we pass in the object as the second argument as 400 and if all of these conditions are not throwing an error or returning in the response prematurely then we can actually add this person as a friend the way we do that is with our actual database so await DP we don't need or fetch radish for this because we're making post requests or modifying data and that is not cached in nextges so we don't need to worry about any weird caching Behavior now to add a user as a friend we're gonna call the S ad so we're adding someone to a set that's what this is doing and we are we want to add where the user is the set oops the session Dot user dot ID and then we want to add add the friends set that's where we want to insert the person that we are wishing to add I did to add so we pass the data that we want to add to the set as the payload in the second argument of this function right here then we want to add the user to the requester's friend list so the other way around if I am friends with you if I'm accepting user friend then you're also my friend right not to get any asynchronous Behavior there if you're my friend I'm your friend and the other way around so we're also going to add to the requester's friend list with sayingdb.s ADD and then instead of calling instead of calling the session user ID right here and the ID to add right there we're gonna switch them around so for the user this is going to be the ID to add the friends set and then the value we're going to pass is the session dot user.id so both people are adding each other as a friend then secondly we want to clean up the friend requests right if you know our um or if you and I are friends now then the front requests should be gone to do that we can say dp.s Ram you know similar to S ad it's SRAM m s r e m to remove from a set we want to remove where the user is ID to ads and we want to remove in the friend underscore requests Department and that should be incoming incoming friend requests department and then we want to remove oops the session dot user dot ID this line is not mandatory and actually that shouldn't be the incoming friend request right now it doesn't really matter if you wanted to also show the outbound friend requests then that would go right here the outbound friend requests I'm going to mark this as a comment for now because that is not a functionality that I've implemented in this prototype it wasn't really necessary and it's pretty simple to implement um if you wanted that that's how you do it but I'm going to comment it out for now because um it's easier to understand if you just don't worry about that for now then lastly we want to remove the actual friend request though and that we can do by saying DB Dot srem and now we are removing the friend request for the session dot user.id under the incoming underscore friend underscore requests and what do we want to remove from the set well we want to remove the person that has added this user the ID to add press save and now this Rod is pretty much done just a bit of error handling and then we're done if the error is in instance of a z dot zot error I've explained what this does previously we're checking if this error is is of a certain shape and we can return a new response that says invalid request payload and the reason we checked if this is a salt error is because of the status that we can send back this needs to go into an object because if this is not parsable we are going to send back a 422 response an unprocessable entity and if we don't if you can't pinpoint what the error is we're going to return a new response saying invalid request with a general status of 400. great let's save that and let's try adding a friend hitting the check mark and it worked so the page refreshed if we have the real-time features enabled and I just reloaded the page that's why it disappeared if we had the real-time features built in already then it would also disappear in real time and now we have added a friend successfully how cool is that we've done different implementation so if you go into our database now and check for the friends then this user is in that user's friend set and similarly it's also the other way around just how it should be however what happens when we don't want to accept the user when some scary ass dude sends us a friend request and we don't want to accept it we should also be able to deny that person right and we haven't implemented that logic yet so let's quickly do that and I think to do the denying it's actually no that's that's we don't even need to um we don't need to copy the so-called the denying route is rather simple let's just create a new folder um and that shouldn't be called friend that should be called deny and inside of the deny folder we're going to create a route dot TS you know the drill by now and because we're gonna make a post request to this endpoint we also want to call the function just that export async function post with a request as the parameter of type request and that is going to have a function body inside of the function body we are gonna have a try catch and first off we need access to the body content and we do that just like in the accept by saying conspody is equal to request dot Json and we want to weight that because that takes a little bit of time and it's asynchronous secondly we want to get access to the session and that is going to be equal to a weight and get server session and pass that the auth options as always now if the user is not logged in if there is no session they shouldn't be allowed to deny anything that's why we're returning a new response saying something like unauthorized with a status indicating that and that being 401 unauthorized and then we can destructure the ID to deny just like we did in the accept route and to do that we can actually copy the One code snippet from there that was some silly stuff added to debug and because I wasn't sure where the issue was and we can remove all of the debugging stuff and so this is the way we parsed the ID in the acceptance road we can copy that and paste that into our deny Rod right here and instead of the ID to add that's going to be the ID to deny and the reason that this is giving an error is because we haven't imported Zod yet so let's import it import Z from Zod and that should fix that error right there great that is fixed and now we can work with with denying this ID and just like we did with the S at adding to a list we can remove from the list of the friend requests so as I brought overview what should happen when we deny a friend is the incoming friend requests that we have they should be taken out of that list that's all we're doing and we can we can do that by saying DB dot s REM we are removing from a set that set being user at the session dot Obsession dot user dot ID and then the incoming underscore friend underscore requests and we want to remove the ID to deny from that right and that's all we need to do so we can return a new response called OK with a status of 200 we could even leave this away actually we don't specifically need to pass a status of 200 um if you just return a response that should be just fine let's see did we return a status here explicitly we didn't so let's not do it here either and then in the error we are going to do the same error handling as in the accept route we can just copy and paste this the console log can stay in there that's totally fine we can save the denial route save the acceptance World great now that we've implemented the denying logic let's try out if it actually works so right now we would probably yeah let's just reset the database for now by typing in flush all into the upstash CLI that is going to clear the entire database of any data and now let's log out go to our login page and in here we are going to log in with one account and then go right back to the login page and log in with another account for example the support at wordful dot AI let's see if that is successfully in the database and it is not so we probably need to log out and let's log in back again with our support account okay so there is an interesting Behavior I just noticed when we log in with one account and then try to log in with another one while still being logged in with the other then it won't count as logging in with that account and instead it will log Us in with another account now that's not a problem because a user will not be able to navigate to the login page anyways when they're logged in because we're going to protect that with middleware just an interesting behavior that I noticed um so let's go to the main page and log out actually click sign out then log in with the other accounts being the support account let's click that and that logged Us in with the other account so let's clear the database one more time and now it should work let's navigate to the login page log in with one the admin account and then let's oh we can just click this button to log out let's go back to the login page and log in with the support account and see if that works great it does work so now we're in the support account and now we can add our other account as the friend let's add them friend request was sent and then let's go back into our other account to see it oops not D login just go to login let's go into our other account to see that friend request it's going to show up in the sidebar here on the left side underfront requests we can see the request and now let's try denying it so let me collect the X and it has successfully denied the friend request it seems again this will be remedied when we do the real-time functionality right now if we refresh the page that would just go away as well and let's navigate into the network tab oh and I think because I just reloaded the page we can't see it which is fine and we can just check the database if we successfully the night that friends if they are not in a friendly summer and they are not meaning that the denying of a friend works great so now we can add friends and we can deny friends and that is the basis for any chat that's about to happen first off however let's fix that issue with the login system a login should always work there should be no issues there and to fix that we are creating a middleware so that logged in users cannot navigate to the login page and log again that doesn't make any sense they should be able to do that instead if they try to navigate to the login page we're going to redirect them right to the dashboard where they belong now to build a middleware in Nexus 13 we are going to create a file in or Source directory at the same level as the app or Pages directory and that file is going to be called middleware dot TS the naming is important we need to name it that you can't just name this what you want so next.js will recognize this is a middleware we're trying to write and what the middleware does is let me show you let's go to excali draw this is an old drawing we can just get rid of this let's just delete it all so what the middleware is going to do is if the client if the person the user let's just put a p in here if the person that is trying to make a request to the login page over here if a person tries to make a request to that page then the middleware comes right in here it checks well let's just put the middleware here first the MW the request goes this way and then first down to the middleware if there is any and now we just created one the middleware checks is this person the user logged in or not and if they are then the middleware is going to Route the request right back to the Dash and I'm just going to put a b dot to the dashboard so the user is never going to even see the login page so the middleware intercepts the request to the login page or to any other page for that matter right and for any route that the middleware runs on it intercepts the request and before anything happens the user is getting put into the spot that is correct that we determine and not to the login page if they're logged in if they're not logged in then they won't have any problem navigating to this page obviously they should be able to log in that's what the middleware does and to create this middleware it's super simple because next auth gives us a cool method we can use and that is called with auth now the Auto Imports don't seem to be working again let's import the width off and that's going to be from next Dash auth slash middleware that's where we get it from and this receives a callback that is going to be the actual middleware let's call this acering function middleware and that receives a request and now we can actually implement the code block that we want to care about however before we do that let's export a const config oops config at the very bottom of the middleware and this config will determine via a match or property that is in a string array in which routes this middleware will actually run for example we wanted to run when a user navigates to the slash so the main home page because that is not compliant with our current routing structure in that case we want to redirect them to the dashboard similarly a login should cause the middleware to run a slash login and then also a slash dashboard slash and then any path and the way we determine any path is by a colon path and then a star so it will run on dashboard slash ASD dashboard slash doesn't exist anything that goes behind the dashboard will invoke this middleware function right here okay first off in the middleware we want to determine the current path the user is on and we can do that by saying wreck dot nexturl dot pathname then we want to manage the routes protection protection let's do that down here we have access to where the user is trying to navigate no let's see if the user is already authenticated cons is off it's going to be a weight get token that's what next auth gives us and it will automatically use our next auth secret that we have determined in the EnV file to decrypt or Json web token and see the values that are in there from the request because that is automatically passed as a token right and then let's determine if the user is trying to navigate to the login page that is going to be rack dot next url.path name or we can just use our path name up here dot starts with and then slash login just for a bit better readability we're gonna save that in memory and then let's determine the sensitive routes and in our case the only sensitive route is going to be the slash dashboard nobody should be able to access the slash dashboard if they are not logged in let's determine if they are trying to access a sensitive route now for the dashboard this would be pretty simple but I want to make it easy extendable for you in the future so let's say const is accessing sensitive route is going to be equal to sensitive routes dot sum so if this returns true for any of the following cases then this this is going to be true for any route if the path name dot starts with and then that route and that shouldn't be as a string but rather as the actual route then we are trying to access a sensitive route in our case that would only be dashboard so if the path name included dashboard then this value right here the is accessing sentence to front would be true next up we are going to determine if the user is navigating to the login page and if they are then we want to differentiate between whether they are authenticated or they are not if they are if is off if they are authenticated then they should be able to access this page we are going to return a next response dot redirect and we also need to import the next response from next slash server redirect the user to a new URL that we're going to construct that's going to lead to slash dashboard and pass the direct.url as the base URL so that's going to be localhost 3000 or whatever and slash dashboard right and if they are not authenticated and trying to navigate to the login page that's totally fine they are free to do so so we're going to return a next response dot next to tell the middleware to just pass along the request in this case the middleware would seem okay the user is not logged in and let the request go back to its original path leading to the login page instead of redirecting the user okay two more cases we want to handle um if we are not authenticated if not is off and we are trying to access a sensitive route we can just copy and paste this value that obviously shouldn't be allowed either so in that case we are going to redirect as well we can copy the return from up here but we're not going to redirect to the dashboard but instead to the login page and we are going to copy this down one more time with shift alt and arrow down the condition being if the path name is equal to the home page so just slash then we also want to redirect to the dashboard because that complies with our current routing structure way better good job we're almost done with the middleware one last thing and that is a little work around that we need to do it's a next auth specific one so for a callbacks that we can pass as the second argument we want the the async authorized and why is that not working we want the async authorized there we go um to return true um there is a node in the GitHub repository this is a workaround for handing redirects on off Pages we return true right here so that the middleware function above is always called if we didn't have that callback we would get an infinite redirect and just an arrow in the browser telling us that this page is redirecting us too often that's why we include this little callback right here now if we save the middleware and navigate to or app let's try to go to the login page we are logged in this shouldn't work we expect to land on the dashboard let's type in the login and we get redirected to the dashboard just as expected similarly when I'm logging out then it's going to redirect me immediately to the login page and we can log in with any other account this is phenomenal because it really removes a lot of um kind of things that don't make sense right um it a enforces our routing structure I'm not able to navigate to just slash instead I'm redirected to the dashboard and then secondly logged in users can't log in again uh you know which is super weird in the first place great so the middleware is a huge Improvement to what our app was previously and now the question is what do we do next so we can see friend requests we can even accept accept them next up would be the actual chat functionality however not with real time yet we're going to add them after um I want you to be able to grasp the concept easily and for that it's better if you split this up in first the hard messages you know persisted in the database and then add the real-time functionality to all the moving parts of our project okay the chats are going to show up right here and let us create the actual page for a chat and we do that in our app under dashboard and in there we can create that page called chat let's create a new folder in the dashboard call it chat and then there goes a page.tsx so in the routing structure as you already know by now this will be accessible under slash dashboard slash chat great this file is going to contain the core functionality oh I just closed my oops I didn't want to do that this file is going to contain the core functionality for our entire chat logic and let me close all other tabs close others let's initialize this as a normal typescript component first and now we can actually work with it now very important because this is a dynamic um chat we need to declare it as such right now this page would be accessible and I think closing the window also closed the dev server if I'm not mistaken yes it did and start the dev server backup and right now if I navigated to slash dashboard slash chat that would work right because we have created that road and even though it does take a bit of loading after spinning up the app we can see it renders page but if I navigate it to you know slash chat and then slash um I don't know random chat which we will use to determine which chat we want to show to the user then there's a 404 so we need to mark this as a dynamic page that can get the current chat from the URL and then determine based on that what to show to the user in Nexus 12 and 13 the way we do that is by marking the chat as a dynamic page segment so inside of the chat and then just having the page shot TSX let's add a new folder and in angled brackets that's how we declare a dynamic route let's have the chat ID and then inside of that chat ID goes the page.tsx and what that allows us to do that little change of inserting one more file with the angled brackets if I now do go to slash chat slash um whatever see what happens it's going to load a bit and then it's still gonna show that page now very crucial is that we get access to these URL values and to determine which chat we want to show to the user later and we can do that through params that are automatically passed to this page and they are passed in the following way we can destructure them as the props and these if you're in typescript are going to be of type page props like that if you're in JavaScript you can just use them right away um if you're in typescript let's determine what these params are and the patch props we are gonna say params and then whatever you put in these angled brackets you can choose the name um freely this doesn't have to be chat ID but it makes sense to name a chat ID however whatever you name this the same thing needs to go right here the same thing is going to be passed as params to this page in my case chat ID that is going to be of type string and now if I rendered all the params.chat ID on the page you would be able to see that I'm navigating to slash chat slash ASD ASD ASD and it's being shown right here so that way we can then later determine which chat we want to show to the user so this is super helpful as for the chat messages that we want to show we can already get started in writing the query for that now we don't have any messages yet but it still makes sense to get them already um so we can say um const ant actually let's destructure the chat ID first I think that makes more sense it's going to be equal to params and then we will worry about the chat um chat messages when we need them first off let's determine the session a user should be logged in when they want to view any chat that is already being enforced by middleware we can also enforce it right here just to have an extra layer of security so concession is going to be and you know what comes next wait get server session and pass that the auth options now because we're awaiting this function we need to mark this as asynchronous and that is going to give us an error on the page that is why we put this in line if you're in typescript so that we can remove all of this and the error will be gone great if there is no session then we are gonna return a not found that we get from next slash navigation suggesting to the user this chat wasn't found 404 um so the user is not allowed or they can't see that there's actually anything hidden beneath the URL that they entered and then we can destructure the user from and I typed out from is equal to session just destructure that great now that we have access to the user we want access to the chat ID and the format that we're going to pass that in is gonna be cons user id1 and user ID 2 in this array syntax is going to be equal to chat ID dot split and then we're going to split that string that we get back as the chat ID at the Double hyphen so what we get back from the chat is a user id2 and a user ID one we can save that so for example if the chat let's go back to excali draw the structure of the chat was Slash chat slash and then comes one user ID user id1 and then dash dash user ID 2. that's going to be how we construct the URL for one certain chat and to make sure the chat is the same for both people no matter who's logged in and you know who you are and who you're chatting with we're going to sort them beforehand because if this was your name and then your friend's name then the chat will be a different one for your friend imagine your user ID was one and theirs was two then from their view the chat will be slash chat slash 2-1 right it would be another thing that's why if we sort this beforehand it would also be one dash and we will do a dash dash two that's how we're going to construct the URLs for the chat and that's why we have the split at the Double hyphen here and getting back both user IDs without currently knowing which one is yours right we can't tell that just by the ID okay and the user should only be able to view this chat if one of these two IDs is theirs one of these needs to be theirs so if user dot ID is not the user id1 and the user I the user dot ID is not the user ID too so if it's if your ID is neither of these then you shouldn't be able to view this so we're gonna return a not found once again syntactically correct of course I can get rid of that great so now we have in short that if you're viewing this chat if you're getting to this point of the code then either of the IDS is yours now to determine which of these IDs is actually yours we can do a simple ternary we can say cons chat partner ID it's gonna be user.id and then if your user ID is equal to user ID one then the chat partner ID will be the user ID 2 and else it will be the user ID one and then to get the name and email and so on for a chat partner we're gonna say const chat partner is going to be equal to a weight DB dot get and we also need to import this now we could also use our fetch redis once again for this one but in this case I didn't experience any caching issue um the user at the chat partner ID as user that's how we get access to the chat partner and then we also want the messages that were sent in this chat right and to get those messages we can say columns initial messages it's going to be equal to oh wait get chat messages and pass the chat ID now this is a function that we're going to create but it's not it's it's not rocket science it's just going to make a call to the database for just these messages I just wanted to abstract it the way because it looks a bit cleaner that way but we can just Define it above the page because at least in this app we're not going to reuse it anywhere else as for the function itself we're going to call async function get chat messages and pass that the chat ID that we want to fetch the messages for and this is going to be a function that fetches all messages using a z range so a a sorted list range with a score range that you're going to see here in a second so we're fetching um all messages for this certain chat room and here we want a try catch to handle any error if it occurs and for the error handling we're also going to put a not found for now and inside of the try the const result that we expect as the messages is going to be of type string array if you're in typescript and an array of messages and the reason this is a string and not a message is because well it is technically of type message as an object this is a Json string that we still need to parse so this is going to be a weight fetch redis once again and this time this is going to be of type Z range so a sorted list range you're going to see what that is in a second it's just a set I'm not sure if we have one in the database currently we don't I'm going to show you that when we get there it's basically a sorted array you can imagine it like that it's a redis structure that we're using here and then we want to fetch the chat and then the chat ID and we want to fetch the messages from that now we are never setting this so right now we couldn't get anything but we're gonna do um in a minute and then we want to fetch from the start index of zero to the in to the end index of minus one so we're fetching all of them we are never stopping to fetch and then the const DB messages let's call it is gonna be equal to results and that's called this results not result dot map and for each result or let's call this message I think it's a bit more meaningful to call this message um we're gonna parse that the Json string that I mentioned to do that we're going to call the Json dot parse method put in the message and then type this out as as message now this message type doesn't exist yet so let's create that in our database.d.ts and one message is going to have the following properties interface message we are declaring this as a typescript interface each message is going to have an ID of type string then it's going to get a sender ID of type string whoever sent the message and receiver ID of type string whoever received the message a text of type string and also a time stamp stamp of type number not as type date but it's time number because this is going to be a Unix timestamp and while we're already here we can Define two short more interfaces one being for the chat let's make one for that interface chat and this is going to get two properties one the ID of string and then secondly the messages that are in this chat of message array so the array of this interface right here and then secondly an interface of friend request the first friend request that we're gonna need for later for the real-time functionality while we're already here we can just quickly implement this it's going to get an ID of type string a sender ID of type string and also um similarly to the message it's going to get a receiver receiver ID of type string great that's it for the the B dot d dot yes that file is completely done we're not going to make any more changes to that even later we are done with that and now we can access the message type that we have just created right down here so now it makes sense now typescript knows aha this is of type message and know what that is and then what we're gonna do later in the video is display the messages in reverse order now this might might sound odd for now but when we're displaying a ton of messages by default the last messages should be shown all right and to do that we're going to apply a CSS trick that's going to reverse the whole messages and to kind of reverse reverse I know that sounds weird but we are gonna re like these messages that we get back are in order right in order of the time that we send them so the most recent message is gonna be at the very top but in the CSS we want the most recent message at the very bottom therefore we're gonna say cons reversed mess or reversed DB messages because we call this DB messages it's gonna be dbmessages dot re not reduce but reverse that's an array method we can call so just reverse the array and then the const messages is gonna be and we are gonna validate this message or these messages to be sure that they are in the format that we expect and we will do that using Zod using a message array validator now to do that we're gonna go first off let's close all the files let's go into or a validators lib validations and then let's create a new file called message dot TS and in this message dot yes we're going to create the validator that's going to parse the messages for us so if we're working with them we can know that they are in the format that we expect now for this validator we are first going to import Z from zot to start or schema validation because with zot we are going to create a schema that's going to validate each message okay and then we can export a constant called message validator you could call this message schema that's um a widespread naming Convention as well and this is going to be equal to a z dot object and inside this object we've already talked about what exactly this is it's kind of like a you know a JavaScript object but instead we get the types of it um so we can parse it later on as you already know this gets an ID of type Z dot string then a sender ID that is also a z dot string we can copy the Z dot string because we'll need it one more time for the text so we can just paste it here and so we got an ID of type string a sender ID of type string a text of type string for each message this is going to be the actual message content later in the chat window and then the timestamp which is going to be a z Oh and I think I'm zoomed out a bit a z DOT number there we go this is the message validator and then we can export another constant because um we're getting back a message array right and this is just for one message so we also want to be able to validate a whole array of messages so we can say the message array validator is going to be equal to a z dot array and then here we can pass the message not the array validator but the normal message validator meaning this is now the message validator as an array allowing us to validate a whole array and then we also want to export the type message that we're going to use later on that is going to be a z dot infer and in here we're going to pass the type off and then message validator this is a super handy utility function that zot gives us to infer the type of the message how cool is that that's the type we can now use and if I'm not mistaken if we go into the db.d.ts it's pretty much going to be this interface right here but as a proper validated type that we can now use instead of that one great so that's all there is for the validator we can go back into our page.tsx let's close out of that and then for the messages to make sure they're of the format that we expect we can say message array validator that we have just created dot parse and what are we gonna parse we're gonna parse the Reversed DB messages we can just pass them in here that's going to parse them and if everything goes right we are going to return the messages from this function and if we don't have any if something went wrong if the parsing failed for example then we are going to return a not found great if we save that and then go back into the browser go to localhost 3000 let's navigate there and see what happens right now we shouldn't be able to see any messages because there are none there is no logic to add a message yet and we are not even showing the chats currently in the um in the sidebar right so we should probably work on that next let's see my notes what's up next I think that would make sense to implement first showing the chats in the sidebar or the friends rather because if there is no chat the friend should still show up right there um under your chats and let's see what I have in here so accepting a friend request and then chatting with database persistence not real time yet okay so the first step to implement that would be to actually list all the friends we have right here in the sidebar so to do that we are going to go into our layout because remember this whole sidebar thing is defined in the dashboard layout that we can see right here under the dashboard that's where we handle the whole friend logic and then currently when we go down to where is it right here chats this user has right now we're just mocking that with a text however obviously we want the actual um friends to show up there and to do that first off we need to fetch the friends right that would be the logical First Step so that we can then show them and to get the friends we're gonna call a function up here in the layout um above the jsx and let's call these friends because that's what they're gonna be the cons friends is going to be equal to a weight and then get friends by user ID that's the function we're going to use to get all the friends by Nadi very reusable so we can call this in any place that we want and then obviously as the name might suggest we're going to pass the session.user.id in here already now as for this function we didn't create that yet we can't import it so let's go under the helpers and did we already create the helpers we did not so under the lib folder actually no we have it right here let's create it in here the helpers directory and then let's call it um get Dash friends Dash by Dash user Dash ID dot TS very verbose I know but it makes sense in this case and then from this file we want to export a function that gets us all the friends by the user ID let's call this um get friends get friends by user ID just like we called it in the layout and this is going to be an async arrow function that receives a user ID as a string type if you're following along in typescript and then in here we want to retrieve friends for current user that's what this function is going to do just for a bit of documentation in here so first off the first step to get the friends would be to get all the friend IDs and then in the next step enrich them with their email and images and so on now we already know how to get the IDS that's nothing new we've done that before const friend and this is going to be equal to a weight um and then fetch redis we are going to use our handy redis helper function and we want the S members right because we are checking if we go into the um up stash console let's navigate to our database the real-time chat YouTube there we go go into our data browser okay and currently there is no set but we are going to store friends in a set which is kind of like an array you can imagine it like that like a redis array um and to get the data from there we use the S members or set members who is in that set and then as for where we want to fetch that data um we are going to put backticks and then user at the user ID colon friends this is the set where we want to get the members from and as of the type that's going to be a string array because we are going to get back all the friend IDs great and now in the next step we want to get the user data for each friend as I said earlier to enrich these friend IDs and to do that we can say Collins friends is going to be equal to and then a weight promise dot all we've been here before at promise.all what this does for us it it um calls everything that is in here simultaneously right so we pass it in Array of promises and all the promises that go in here are called at the same time because they don't depend on each other so if I fetch one friend ID at the same time I can fetch another because I don't need to fetch one before I can fetch the other that doesn't make sense so we can just speed this up a lot by catching or by querying all the friends simultaneously we do that by saying friend IDs dot map and then for each friend ID an async arrow function is going to be called that receives the friend ID as its parameter and then we are calling the following function block and did I mess this up it should be friend IDs there we go and then here we can say Collins friend is going to be equal to a weight DB or actually let's just use our fetch Reddit helper for this as well fetch redis and then we want to make a get request to the database and as for the where we want to get the data it's going to be the user at the friend ID so we're fetching all the information that is linked to this certain friend and then we can return friend as user great so now we are casting the type and why does this not work it says friend any that is weird so how about we do that earlier we just cast it up here as user and now that should work great okay that's it for this function um almost you just want to return one more thing because right now this function doesn't receive it doesn't return anything it's just avoid because we are not returning anything we can do that by saying return friends right here at the bottom so we are returning the array that we are fetching simultaneously and now we can go back into our layout and actually call this function from our helpers and get access to all the friends that this person has now if they have no friends at the moment if they're new to the application then we don't want to show the your chats currently we are showing that all the time but there is no need to show this if the user has no friends yet so we can conditionally render this by saying friends dot length is greater than 0 and if that is true then only then we are going to render this div right here and else we are just gonna render no so nothing will show up and only if there are friends this diff will be shown and then similarly we want to go into the chats that this user has to show um well the the friends that this user has has access to and we want to do this in a client component as well because later on we want X we want to expand on that and also show unseen messages in real time right so it makes sense to do this as a client component because we need to do that in order to subscribe to real-time updates so instead of the current um just the text that we had right here let's render a component a client component that we can render out to display all the current chats this user has and let's call this component the sidebar chat list now we can save the layout for now the sidebar chat list does not exist yet we can go out of all the other pages and now work on creating that component so let's go into our components directory and then create a new file called sidebar chat list dot TSX great and inside of this sidebar chat list we are going to initialize this as a regular functional component this is going to receive props later so these and that interface can stay for now and first off let's work on the jsx just so we see something that resembles the chat list and we are going to go we are going to get started with a UL an unordered list and this UL gets a roll of list and then also a class name of a Max Max Hyatt of 25 REM so if a user has a lot of friends it shouldn't you know push all the other content we see in the chat bar um well we can't see it right now but it shouldn't push all the other content way below the screen that would look horrible instead there should be a scroll bar instead great and then to actually initialize the scroll bar or allow for the scroll bar we are going to put a overflow y of Auto and then for um some additional styling we're going to put a minus margin X of 2 and a space Dash Y dash one to give it some vertical spacing and then as for the friends we are going to receive them as the props remember we want to display the friends in this component so we need to fetch them in the server side layout pass them down to the sidebar chat list and as for the type that the friends are going to be it's going to be a user array so now we know the type and now we can actually map over those in the jsx so let's say friends dot sort dot map so the friends are always in the same order and then for each friend we want to execute a code block for now we don't want to return immediately because first we want to determine how many unseen messages are there for this friend right so for now let's just comment that out so we can take a look at the sidebar together let's import this component like that and we also need to pass the friends so friends is going to be equal to friends that we fetch from the database let's save that reload the page and now we can see the sidebar by the way there's a little Optical bug that kind of looks weird let's figure out why that is so we have the LI element right here and then that's a separate Li element I think that's the problem so let's move the friend requests out of the LI element go into our sidebar and let's try this does that work is that now moved into the proper position oh that did not work let's put that back and then I just saw that we need to close the UL right here so let's finish the UL that is going to give us an error so let's go down here and that UL should not go there let's save that and hopefully that's going to no that did not to see that did not seem to fix the bug instead let's move this Li right here that contains the friend request sidebar options and by the way let's revert that the UL should actually um close there right before the nav and that's even yeah let's go back to that so right now we didn't change anything and then let's move this Li right here that's what happens and when you don't just stupidly code out the component you have prepared but instead you do it you know step by step sometimes this can happen um so let's move this Li to the same level as these brackets right here so that we have the LI then the URL closes and then another Li closes now if this confuses you just look into the GitHub repository um obviously it's all in there and I hope it didn't I hope I explained it well so the only thing we did is we move this Li to the same level as where we are mapping over the sidebar options let's save that reload the page and hopefully now that will look better and yes it did that fixed the issues so now we have the add friend and the friend request at the same level and it looks way better here in the sidebar great okay that's just one little fix for the CSS and now um what I want to show you is that for each friend we are not displaying them at the moment just like when we get a new friend request here in the sidebar you know the little icon that shows up right and we want to show that for each friend if we have an unseen message from that friend that's the goal right now and to do that we need to find out for each friend how many unseen messages we have for that exact friend and the way we do that is first off we can pass the Unseen messages into this component and then see for every friend if the ID is in there and how many times does the ID occur in the Unseen messages now to know how many unseen messages we even have let's put that into state so we can change it later on so and first off let's just return some random stuff in here so the arrow is gone um so okay as for the Unseen messages let's put them in States so we can change them later on and let's call them unseen messages that is going to be of type message oops message array and we need to import the use State and now for these messages I have made the decision to just keep it keep them in state now we could also Implement a functionality where you could also show the messages that are on scene from when you were offline in our case because we're just keeping them in state these are only going to show the messages that you receive while you are online okay and for the messenger right this is going to be initialized as an empty array for now and so we can push any unseen messages that come in during you know run time or while the users active on the page so we can display them in this state um also we want access to the um router for later and so we're going to say cons router is going to be equal and I switch to English keyboard again it's going to be equal to use router so we can then determine if the user has seen the messages right and we do that by checking if the path name is the chat you know it's kind of hard to explain but if we navigate to a chat we get access to the the chat ID from the URL right and by doing that we can check if a user has seen the messages in this chat ID or not that's why we want access to the router and this comes from next slash navigation and not from next slash router remember that and then as for the path name const path name is going to be equal to use path name which also comes from next slash navigation this is going to be the relative path so for example you know slash dashboard slash ad and not the whole localhost 3000 thing as well now to determine if the path has been checked we are going to create a small use effect that is going to run every time the path name changes so that's why we're putting the path name in the dependency array so every time this value changes in this function the use effect is going to get run and then if the path name dot includes the chat then we're going to execute a call block so if the user is in any chat then we want to set the Unseen messages to whatever they were previously and we're going to um no we don't want to set them to whatever they were previously we want to get access to whatever they were previously and then you're going to return the filtered version of that so we're going to say pref.filter and for each message that we get and something is off about this syntax there's a parentheses missing there and what did I mess up let's take this out for a second that shouldn't be there there we go so we're going to set the Unseen messages to the filter of the previous one and then for each message that we get and we're going to check if not the pathname dot includes the message dot sender ID and that's the way we figure out if the user has seen the messages or not and if they are on the corresponding chat then we'll take those out of state um because then they have seen the messages okay we're going to implement the real-time features later together don't worry it's not super Complicated by the way it's actually pretty cool and pretty fun to do that for now let's work on the jsx a bit further so after mapping the friends we want to display something for each friend however first off we want to determine how many unseen messages there are for this particular friend to do that we're going to say unseen messages count is going to be equal to unseen messages dot filter and then for each unseen message we are going to return a call to block and for each unseen message we're going to return unseen message dot sender ID is equal to the friend dot Eddie so the person that we're mapping over and this needs to be unseen messages dot filter and then we don't care about the actual messages we just care about how many those are so we're gonna put a length at the end of that and by doing that we get access to how many unseen messages there are for just for this friend and now we can get into the GSX part for each friend because we are in an unordered list we want to return a list element that needs to have a key because we're mapping over elements and in this case that's going to be the friend.id inside of this we are going to render out an a tag and we do this instead of a link because every time we visit the friend we want there to be a hard refresh to get the latest messages this friend has sent and an anchor tag compared to a link tag forces that heart refresh of the page that's why we're using that and the href of this anchor tag should be a template string that we put in the curly braces leading to slash dashboard slash chat slash and then to the corresponding chat ID however currently we don't have a way to get that right and to get access to the chat ID that we want to navigate the user to we are going to create one very tiny helper function that is going to be the chat href Constructor meaning we pass it sounds fancy but the only thing it does is it takes in the front ID and the user ID and sorts them separates them by two dashes so we can have the chat ID for the URL as we talked about earlier that is corresponding to the chat structure we talked about that so let's go into our utils under lib utils and create this function that is going to be an export function chat href Constructor again sounds very fancy but the only thing it does is it takes in id1 as a string takes an id2 id2 as a string and then the we are going to sort the IDS so const sorted IDs is going to be equal to an array that contains the first ID and the second ID and just sorts them standard stuff and then we are going to return a template string to put those IDs into the URL later which is going to be the sorted IDs at the index of zero then a dash dash and that has to go outside of that a dash dash a double hyphen then sorted IDs at the index of one that's the only thing this function does again sounds fancy very simple functionality and now we can call that right here and the chat href Constructor and pass it for one the session dot user dot ID and then why is that not working chat href Constructor that should be working and secondly we pass a different Dot ID and there needs to be a comma there and did I okay so we don't have access to this session so what we're going to do is because we already fetched the session in the layout we can just pass it as a prop to this component so let's go to the sidebar chat list and give it access to the session ID which is going to be equal to the session.user.id now the component the sidebar chat is doesn't receive that yet doesn't expect it so let's accept the session ID as a prop that is going to be of type string that needs to be the session ID and now the error should be gone we have access to the session ID and we're going to pass it right in here to be able to construct the URL for this component or for this chat rather so what we just did is let's just have some text in here hello for example now for each friend we have that should be displayed in the sidebar and when we click on that okay and we need to mark this as a client component as I said earlier this needs to be a client component because we are going to implement real-time features later so we're going to say use client at the very top of the file that's how we make this into a client component and then the for each chat that would be displayed up here in the in the corner and then when we click on that the um the chat will be opened right here on the right side as the separate chat page the layout will stay because it's a layout and to test if we did everything correctly let's add a friend let's add my other account admin at wordful.ai press add and then let's log out log in with the other account except that friend request and then hopefully they will be listed as a friend we can see the friend request coming in we get a react hydration error that's fine it just sometimes does that we can press accept that's going to accept that as a friend and now we can see Hello however we still need to hard refresh the page um for the friend request to be deleted and also the little icon going away that will be not the case later however right now we can see we are rendering out a hello here when we press it that should take us to the corresponding chat so let's press the hello see what happens and that should put the chat ID great but we get an undefined interesting so we get access to the this ID right here the zero B let's see what user that corresponds to that is the zero B that's the admin at wordful.ai so it works for the for the admin but it does not work for the friend ID currently interesting so we don't get access to the friend dot ID apparently that doesn't exist to see why let's go into the layout components and take a look at why that could be so we get the Friends by user ID so the error should be in here and what I imagine is the case is that the friends we get back here is not a user but instead a string because we're making a get request so this is probably going to be a string and now we can say the parsed friend um so I expect this to be a Json string const parsed friend is going to be equal to the Json dot parse we're going to pass it the friend and that is going to be a user and we're gonna going to return the post friend let's save that see what happens let's click on the hello again and that was the case okay so um we got back a Json string from the database that's not ideal we still need to parse that and by doing that we get access to the to the other idea great so it's put into the URL correctly now what we need to do is first display the actual friend name and not hello and then we can get to the chat functionality and for now we don't want to display hello here that's not ideal instead we want to display the actual friend name and we get access to that on different object because we enriched it with all different properties and then next to the friend name right now we're only displaying the name wordful AI support for example and next to the name we also want to display how many unseen messages there are for this person we can do that down here but we're only going to display that if there are more than zero so if there are unseen messages count is that if that is larger than zero then we're going to render out a div and else we are going to render out null inside of this div this is going to get a class name of background Dash Indigo Dash 600 a font Dash medium a text of extra small and I think we can just copy and paste this actually this is a class name that we have used previously inside the no we haven't okay let's just type it out then it's going to get a text of extra small a text Dash white a width of 4 a height of four so this is going to be the little icon you can see that this place how many messages there are a rounded Dash full Flex justify Dash Center for horizontal alignment and item stash Center for vertical alignment and that's it inside of this diff and there needs to be no space at the end right there inside of this div we are going to render out how many unseen messages there are for this chat and we've already calculated that up here so we just need to display it put it in here and right now that shouldn't be rendered because we have no unseen messages for this user great now right now this text still looks a bit weird let's give that a bit of styling so it fits more into the year the other studying and to do that we're going to give the anchor attack itself a class name and that class name is going to be text Dash gray-700 when we hover over this we want the text to be Indigo 600 when we hover over this we want the background to change the back add to BG Dash gray Dash 50. we want this to be a group then we want this to be Flex within items Dash Center a gap Dash X of 3 so it only applies to the horizontal axis we want this to be rounded Dash medium makes it look better if you navigate to it via the keyboard for example a padding of two a text of small we want a leading-6 and then a font Dash semi bolt it's going to make it fit in better with the rest of the starting as we can see if we hover over if we hover over it it gets Indigo just like with the other ones perfect so we created the sidebar we are making really good progress by the way good job so far really really nice that is pretty much the sidebar done the only thing that's going to be left for later is adding the real-time functionality which is going to be super fun that is going to be very enjoyable to do and pretty straightforward as well nothing very interesting for you to learn about it for now let's actually get started with a chat we can add a friend we can request a friend um to give this a bit of padding we're going to do that in the layout so it's going to look better very very simple fix and we currently have the chat and we sometimes get hydration arrows that's totally fine those won't be there in production okay as for the chat let's get started in creating that we can see currently if you go to the page.tsx corresponding to the chat ID the only thing we're doing is displaying the chat ID which is not ideal instead we want um to display the messages and then also a field that users can use um to type a new message let's get started with that first off we want to keep the div and this is going to get a class name of flex-1 a Justified Dash between and then a flex Flex Dash call so what they justify between does for us if we use it with a flex column then vertically the justify between is going to apply if this wasn't Flex column then the flex at the justify between would apply horizontally and what it does is it pushes the element as far away from one another as the layout allows them that's why we have to justify between and there this gets a height of full and a Max height that we're going to calculate and we do that by putting it in these angled brackets meaning we can insert any value like one ram for example and that's going to get applied in our case we're going to say calc which is going to calculate the height dynamically and we want to put in here 100 VH 100 view height minus 6 REM because that's gonna fit or lay out very well right now that won't do much but as you're gonna see with the later messages they can't go beyond the screen then that would look super weird and to prevent that we apply that styling and then inside of this we want to get rid of the power we want to get rid of the params.chat ID for now and then continue with the next div now because the chat is super important there is going to be more CSS involved in this more styling but in like in return for that in the end it will actually look good and you'll have a proper chat so I think it's definitely worth it way better than just doing some prototype where yes you can see the messages but nobody would ever use it because it looks horrible so let's get into The Styling a bit more the second div is going to get a class name of Flex from small devices and up we want an items Center for um for vertical alignment a justify between and just hit that a padding y of three a border Dash B-2 so this is going to apply the Border only to the bottom of this div and a border Dash gray Dash 200 inside of this div is going to be another div with a class name of a relative Flex items Dash Center and space Dash x-4 to give this a bit of horizontal spacing and inside of this div is going to be another div with a class name of relative and then inside of this div we're going to hook up we are going to put one last div that is going to be an image container that's going to contain the image of our chat partner we're just going to get a class name of relative width of 8. on small devices we want a width of 12 a height of 8 and on small devices and up obviously we want a height of 12. great inside of here we're going to put an image the next slash image I've explained what this does previously it's kind of like the HTML image component but it's automatically optimized which is super cool we can give this a fill property so it will always fill up this div remember this this needs to be relative for it because that will apply an absolute styling to the image and if we didn't apply the relative it would just stick to something else this image needs to have a refer oops referrer policy of no dash referrer so the Google Images work just fine with it the source is going to be the chat oops the chat partner dot image and we want to display who this person is currently chatting with the alt is gonna be in curly brace is a template string we can say chat partner dot name profile picture to make this accessible to more people for example visually impaired and then a class name of a rounded Dash full great that's the image done all set we want to go one div below two divs below and then open another div up so we have three divs below that and this these two divs now this is going to get a class name of flex Flex Dash column and leading Dash tight and the reason we are initializing this div or creating this div is to display the user's name in here inside of this diff we're going to create one more div with a class name of text Dash XL flex and item stash Center and then inside of here a span to render out the actual name we can put the name in here by saying chat partner Dot and then oops not the image we want the name and this button is going to get a class name of text Dash gray-700 a margin right of three and a font semi-bolt great that's the name done then when go to the end of the closing span take one div down and then create one more span tag so we have four closing divs below this span with a class name of text Dash SM for small and a text Dash gray Dash 600 so a bit lighter than the name and here we're going to say chat partner dot email we can't save that let's take a look at what this currently looks like without the page and we can see there is well we still get the hydration error when we click on the page too early it's not going to be a problem later but this is what we just did we displayed the user's profile picture the name and then their email in a bit lighter color the padding is going to be applied from the layout later we don't need to worry about that on a per page basis so the reason we are creating the padding in a layout later is so it will look unified across our entire application great good job very very good that's most of the styling for this component done the only thing that we want to do now is render out the messages and the chat input to do that we are going to create a custom component go to the very end of the jsx and then one closing div up and this is where we are going to create the messages component this component doesn't exist yet but it is going to contain all the messages that this chat has ever been sent and we're going to worry about the chat input later first off let's create that messages component that doesn't exist yet we're going to go into our components directory and then create a new file called messages.tsx and this is going to be you know when we see the chat okay messages is not defined we can already import that after initializing this as a component we obviously first need to export the messages so we can import them in here when we save that we won't be able to see anything yet except for the messages and the go away hydration error we know the messages are going to be shown here currently it just says messages let's get to work in that component we can close all the others for now and close the sidebar and then in the messages component we are going to create a div we've already done that through initializing this component given give it an idea of messages so it's a bit easier to see when you go to the dev tools if you're trying to debug where the messages begin it's just kind of clean to have a ID for that and then as for the class name um we're going to give this a flex a height of full a flex-1 and now something very interesting a flex Dash call Dot reverse and this is the reason why we reverse the messages in the first place if we go to the chat um page where we get the messages remember we reverse them and the reason is this Flex call reverse right here it turns everything upside down meaning the bottom of our messages is going to be shown by default because it's kind of considered as the top because we reversed it that's why we apply the effects called reverse a gap-4 a padding of three an overflow of Y dash Auto so we get a scroll bar if there are too many messages and they don't bug out of the screen a scroll bar Dash thumb Dash blue this is going to be a long class name is scroll oops scroll bar Dash thumb Dash rounded now we're starting the scroll bar afterwards this is going to be a scroll bar Dash Track Dash blue dash lighter a scroll bar Dash wwm is robot-w-2 and a scrolling Dash touch now if we hover over these we can see these are not tail one classes because this would be kind of hard to do in Tailwind these are custom classes that we still need to Define and to do that we're going to navigate into our globals.css where we are going to Define these Styles these are just um four CSS classes very simple to do so for the dot scroll bar Dash w w I keep messing this up for the dot scroll bar with two um this is going to be a colon colon dash webkit Dash scroll bar this is something CSS gives us by default super handy and as the name I suggest we're going to apply a width of 0.25 REM and then a height of 0.25 RAM and that's it for this class secondly for the dot scroll bar Dash Track Dash Brew blue dash lighter lighter there we go we get a web kit Dash scroll bar Dash track again super handy and here we're gonna put a dash dash BG Dash or opacity of one a background Dash color of hashtag F7 and then fafc and then lastly a background Dash oops Dash color of rgba and then as the first value a 247 then secondly a 250 a252 and then as for the opacity we want a variable that we pass the opacity we have defined right here which is in our case is going to be one okay then for the dot scroll bar Dash thumb Dash value um no that's not going to be value that's going to be blue for the scroll bar Dash thumb Dash blue we're going to give this a web kit Dash scroll bar Dash thumb so we can start that again we can copy what we have in the scroll bar track blue lighter paste it here and the only thing that's going to change is this one and then the rgba as well as for the hex value this is going to be Ed F2 F7 there we go and then for the rgba values that's going to be 237 for the first 242 for the second and 247 for the third and then one last thing we want to do one last class is going to be the dot scroll bar thumb rounded rounded and we also want access to the web kit scroll bar uh thumb there we go and we're going to apply a little border radius of 0.25 Ram to that great that's all the CSS we're gonna do save that and we're out of there um by the way if that was too fast if you want to copy anything um here's the CSS if you're following along and missed anything and but again everything is in the GitHub repository if you need it let's switch back to our messages and now go into that messages div great so now this will look good no matter how many messages there are in here we're going to create a div that is self-closing with a ref of scroll down ref the reason we are creating this div at the top of the component is first off we reverse the order so everything is in Reverse keep that in mind and then secondly when we send a message we automatically want to scroll to that message and the way we do that is through assigning a ref to this div right here at the very top and then we still need to create that ref let's call it scroll down rev from scroll down ref is going to be equal to use ref and then for the use ref we're gonna pass it null and if you're in typescript this is going to be a HTML div element or null because we're using a ref we also need to turn this into a client component because we're making use of any hook this needs to be a client component the stiff now knows what it is and to actually achieve the scroll down effect um we're gonna we're gonna do that later when we actually can send messages I think it makes more sense to do it later just so you could see what it actually does then below this for the messages we're gonna map over them however we currently don't know which messages there even are right and to get access to those we are going to pass them as a prop into this component so inside of the props that we receive one of them is going to be the initial messages and we also need to Define that in the interface in initial messages if you're in typescript and this is going to be of type message array great and the message type is going to come from or validator and not from the interface we have created in the d.ts file and then the page will throw an error that it doesn't know um well we are not passing the initial messages that's the problem we have already defined those in this component though so we can just pass them we can save that and then let's go back to the messages and now let's map over every message however the reason I call this initial messages is because as you know the drill by now we want to add to it later so first off we're going to put this in state let's call this messages and this is going to be of type message array and then in here we're going to put the initial messages right so that allows us when a user sends a message we can put it into the state showing it directly to the user instead of having to refresh their page or something and then we can take these messages and map over them down in the jsx so messages dot map and for each message we want to get access to that message as the first parameter and then secondly we get access to the index and then we are going to execute a code block instead of directly returning some jsx because first off we want to determine if the message that we are displaying this message instance is sent either by us or by our chat partner because either we want to display it on the right side or the left side and in a different color and to do that we can say is current user is going to be equal to either the message dot sender ID and we need to check if this sender ID is equal to the user that is currently logged in is equal to the session ID now to get access to the session ID we could either use client-side use session but because we already fetched it in the parent component we can just pass it down as a prop this message is going to get x to the session ID and the session ID is going to be of type string great now this is still throwing an error because we're not returning anything that's fine for now let's navigate back to the pages and provide that session ID to our component it's going to be the session.user.id great we can save that now we get access to the session ID and now we want to check for displaying the image if the um so if there are multiple concurrent messages from the same user because if there are the image that's going to be shown at the bottom of each message and it should only be shown for the last message that's what we need to know is there a next message from the same user to do that we can say has less has next message from same user is going to be equal to and then we're going to check the messages at the index of the current map index -1 the sender ID is that equal to the messages oops messages at the current index of dot sender ID and if it is then that means yes there is a next message from the same user the styling is going to be done in the jsx that we can get so now that's all the logic we're gonna do and then we are going to return a div at the top level this diff needs to have a key because the first element after mapping over something always needs to have a key so react knows what you know what element that is and this is going to be consisting of the message dot ID we're going to put this in a template string and then the dynamic first value is going to be the message dot ID Dash and then the message dot timestamp that will create a unique key for every message and this is going to get a class name of chat Dash message we could also put this as an ID if we wanted to and this is just a bit easier in the Chrome Dev tools to see the class name so we know this is one message instance then inside of the stiff we're gonna put another div where they class them off and this is going to be conditional because if the message is from the logged in user we want to display it on the right side if it's not we want to display it on the left side so we've already done the check if this is the current user and we can use that to our advantage Now by giving this div a conditional class name in curly braces we are going to use our CN helper function for that that help function is going to come in super clutch in this component because there's going to be a lot of conditional rendering involved depending on who sent the message this is going to be getting a class name of flex items Dash end and then as the second argument an object and a Justified Dash end if is current user is true so only if this value is truthy then this style will also be applied and justify the message to the end because else by default it will be at the start so on the left side and then if it's the current user then is it going to be displayed on the right side inside of this div goes another div lots of divs in this component but it's going to make the final chat look super good I promise this diff is going to get a class name off and this is also going to be conditional CN and the CN gets the default values of flex Flex Dash call a space y of 2 a text Dash base a maximum Dash w x s so it can't exceed a certain width for each message and then in MX of 2 just to give it a bit of um margin and these classes are always going to be applied and then let's do the conditional ones in an object where an order dash one and items Dash end is applied if is current user and also we want an order Dash 2 items Dash start if not is current user okay great after that we are going to create one span element this is going to contain the actual message text that we want to show and the class name is going to be a very interesting construct for this one it's also going to be conditional and as for the values that will always be applied it's a padding X of four a padding y of two a rounded Dash large and then inline Dash will lock great then for the conditional ones let's pass an object as the second parameter and if we have the current user then we want to apply a background of indigo Dash 600 and a text Dash white for each message if is current user secondly if this is not the current user we want a background that oops a BG Dash gray-200 a text of Gray 900 and that is if not is current user and then as the next conditional we want a rounded Dash bottom right dash none if is well if this value the has next message from same user is not true so not has has next message from same user and is current user so only um the last message that is sent concurrently from the same user has a border like an etched border and all the other ones are going to be around it's just a very tiny styling detail that you could easily omit if you wanted to um I think it's just a very nice touch and then um similarly we want to apply a rounded off bottom left Dash none if not has next message from same user and not is current user those are the conditional classes that we want to apply to the span element and inside of here as I said just a minute ago we're going to render out the message dot text after the message.txt we're going to render out a space and we do that in a curly brace so it's always shown and not dependent on HTML so we're kind of inserting a JavaScript space here and then we want to display the time this message was sent in a span with a class name of margin left Dash 2 text of extra small because this is not very important in the text of Gray 400. inside of here we are going to format this timestamp we don't want to display it as a number if we did let's see what happens message dot timestamp now there is no message yet and that would actually make sense to do the message input first so we can actually write messages and we can see what we are doing um so let's pivot real quick I thought we would create the whole message component first and it's not that much left um however I think it's easier to see what we're doing if we can actually send messages so let's continue this component here in a second first off let's create the chat input in our page and by doing that we can actually send messages I think it's just going to be a bit more intuitive if you see what we're even doing in the messages component so let's go to the chat chat ID real quick and then below the messages we want to render out a chat input that component doesn't exist yet so let's create it under Source components then a chat input dot TSX initialize that as a functional component if you're in typescript and inside of this component we can already get started with the jsx really quick as again the reason we're doing this now the reason I pivot it is because um by doing this we can actually send messages and then we can see those messages Jesus Christ we can see those messages and actually see what styling we're even applying it's just better to show you than to explain to you so first off this is going to get a div with a class name of Border Dash top a border Dash gray-200 well not border top just border Dash t a padding X of four a padding top of four a margin bottom of two and on small devices end up we want a margin bottom of zero one more div one last div in here with a class name of relative a flex Dash Flex dash one so it's going to take up all the space it can get inside the flex element an overflow of hidden rounded Dash large to give this a large border radius a Shadow Dash small a ring of one a ring Dash inset a ring Dash gray Dash 300. now this next element is a pseudo selector that Tailwind gives us access to it's the Focus dash within so if the focus is anywhere within this element we give it a ring of two and then when the focus is with oops with in then we want to give it a ring Dash Indigo Dash 600. great okay that's all we need to do for now um go into that div and inside of this div we are going to render out a text area however we're not going to render out any text area we're going to render out a text area that changes its size depending on how much text is in there because else it would look super weird and to do that there is a super cool super convenient package we can use called text area Auto size and that is on npm it's called react text area Auto size has 2.5 million installs and is super easy to use as you can see we just use it like a normal text area but it changes its size depending on how much content is in there that's why we're going to be using this let's stop the server for now type npm install and then react text area Auto size hit enter that's going to install the package for us it's a very small lightweight package we can use and we can copy the import statement from the npm documentation and now import that in orchat input to then render it out inside of the two divs that we have created here the text area alter size great this is going to be a self-closing component with a ref of text area ref we want a ref to this text area because we want to be able to focus it programmatically later let's create the ref const text area ref you know how this works but now is of use rev know by default if you're in typescript and html text area element or no and because we're using a hook you know the drill use client at the top of the page great then on key down we want something to happen we want to have a function that sends a message and whenever the user presses enter that we get access to in the first parameter the event we can do a check if the person just pressed enter by executing inline code block and we're checking if the E dot key is equal triple equal to enter with a capital e and if the user did not press an e dot shift key because if they're pressing shift and enter that means they want to go into the new line if they just press enter they're going to send the message and we're going to prevent the default behavior from this event by calling e dot prevent default and then call the send message function doesn't exist yet let's quickly create it cons send message is just going to be an error function that doesn't do anything for now just to get rid of the error let's format that and then the rows in this text area are going to be one the value is going to be input and this doesn't exist yet we are going to create a controlled text area meaning we keep the content in state the state as you can already guess we're going to name input it's it's going to be of type string and then empty string by default we need to import the U State and to make this a controlled component we also need an on change Handler where we get access to the event and the first parameter and then set the input to the E dot Target dot value this Ito target.value if you don't know contains the text the user has typed into the input field and we are saving that into the state and then I'm telling the text error you should have the same value as in the state so whatever is in the text area is always going to be the exact same as in the state that's the purpose of a controlled component the placeholder is going to be a template string that's where we're putting this in curly braces message and then we want to display the name of whoever this person is chatting with right so message wordful AI admin for example to get access to that we need to have access to the chat partner and because we already fetched that in the parent component we could pass that down as props to this component and that's precisely what we will do so chat partner is going to be of type user that needs to be capitalized and then we can first import this component in our main page and also pass it the chat partner so we can now go back into the chat input and display the name of the chat partner right here it's going to be chatpartner.name so for example message wordful AI admin that's what we're doing here and then as for the styling of the text area this is going to get a class name of block width of full resize Dash none because it's automatically going to resize border of zero a BG of transparent a text Dash grade Dash 900 Place holder this is a pseudo class we get access to from Tailwind so we're gonna style placeholder with a text Dash gray Dash 400 then on Focus we want to give this a ring Focus colon ring Dash zero on small devices and up we want a padding y of 1.5 then on small devices end up we want a text Dash small and on small devices now we want a leading dash six and that's all the studying we want to apply for the text area let's just take a look at what we have created until now and restart or refresh the page okay we installed a dependency so we need to restart the dev server start the dev server back up go back into a browser and take a look at what the chat input currently looks like give this a hot second to load and now we can see the text area is right here and if we type into it it resizes beautifully great there is no button to send it yet but we can already type in messages if we hit enter or send message function gets called but it doesn't do anything yet but very nice that's going to be our chat input really really good we're making good progress if you're following along good job let's continue with this component there is not so much left actually um just one more button and that should already be it and one more div so we want the button to be in the bottom right corner of the component and to give it a bit of spacing vertically as you could see right now the text area is super small it just likes looks like a regular input we want it to have a bit more height and to do that let's put a div under the text area Auto size and if we click this div then we want the text area ref that's why we put it in the ref dot current dot Focus to be called the current is optional so we need to put a question mark there um and then this is going to get a class name of padding y-2 oops class stem is equal to padding y-2 and area Dash hidden of in string true to hide this for accessibility purposes and because this is just for decoration and visually impaired people people navigating with um screen readers for example don't need to see that they probably don't want to see that then a div in here oops and we need to put that right in here between the two divs a div with a class name of padding Y dash PX we just give it one pixel and then a self-closing div in here that gets a classm of height-9 so let's restart this we gave the text area a bit of height and now we can put the send button at the bottom right because it has because we can't type there if I were to type the space at the bottom of the stiff is always going to be free we can't type here because we inserted this div with a height of 9. that's the purpose but we can still click it and it will still Focus the text area just fine that's the purpose of this line right here of the on click and then below that we want to put the send button so with two closing divs at the bottom of the page let's initialize a new div with the class name of absolute a write-0 a bottom Dash zero a flex just oops justify Dash between padding y of two padding left of three and a padding right of two then inside of this div one last div for this entire component with a simple class name of flex Dash shrink Dash zero so not let this get any smaller than it is and then here we're going to put a button actually let's put a custom UI button in here we've already done the reusable button put that in here and depending on whether the message is sending or whether it's not sending we want to display a loading State we're going to do that later but this button is going to say post for now then if we click this button on click we want the send message function that we've already mocked out to be called this button is going to be of type submit there is no form but it is a submit button um and then as for the class name this is already done you can just save that restart the page and now we can see our big post button right there in the corner hydration error can go away right there in the corner is a post button we can access it via the keyboard we can press it it will call the function but the function is not going to do much yet however we want to keep track of whether this is loading or not is the message sending and to do that that loading state as the name might suggest we're gonna keep that in state that is going to be called is loading set is loading it's going to be of type Boolean and by default we don't want the state to be true and then we can pass that is loading to the bottom is loading is going to be is loading and just to show you what this does I can default this to true for now so you can see that the post is loading beautiful let's put that back to false however we don't want this to load by default and now in the send message and this is going to be a very simple function first off no matter what happens we want to set the is loading to true because we're about to send a message no matter if it fails or not and then comes a try catch block first off we want to make a post request to the endpoint that's going to handle the sending of a message to do that we're going to say await and we need to mark this as an asynchronous error function to do that axios we're going to make the post request with axios DOT post to the slash API slash message slash send endpoint and as for the data that we're going to pass we're going to pass a text and that is going to be input and we also want to pass the chat ID to that endpoint however the chat ID doesn't exist yet so we also we know that from the parent page component right so it would make sense to pass the chat ID in here we also need to Define it in the interface and that is going to be of type chat ID of type string so now we get access to that and we can put it in the pulse request to know for which chat this message was sent where it belongs to so now we get an error in the page.tsx and that is because we also need to pass the chat ID and let me pull up my guideline here now that's the YouTube Project that's my guideline over here let's see where the chat ID is coming from we need to pass that from the page.tsx and the chat ID we've already figured that out in the page.tsx is just the chat ID great we can save that and now let's go back in here reload the page we can say something have that reload with a very big image click post and that is going to call the API route now obviously it's going to just show a loading State because we're setting the low to true but the API Rod itself does not actually work so when I read out the page type something in here click post well we get the hydration warning we don't care about that we just want to care about the fetch request so let's try that again and there's a 404 error on the send API road because that doesn't exist yet but we can kind of send messages the whole chat input is done which is great we just need to work on the logic to handle um the actual message however before we get into that let's quickly finish up this component it's only a few more lines of code missing first off um after posting the message we want to clear the message field and because we have this as a controlled input the text area it's a huge Advantage because now we can just say set input to an empty string and therefore we have cleared um the the input let's quickly mock this call let's await a new promise that promise gets a resolve method and we can set a timeout of 1000 seconds and then call the resolve method afterwards resolve there we go why doesn't that work let's quickly enable GitHub co-pilot it will just get the syntax right there we go okay first yeah that's the syntax okay never mind let me disable GitHub co-pilot again what I want to do right now is mock the API response so we're just waiting one second and then we're pretending like the API route got called successfully so when I say hello get rid of that error when I say post we wait one second and then the input field should get cleared and that doesn't happen because I think the hydration error interfered let's say ASD click post and now the message field got cleared after one second and we also want to reset the loading State now to do that we can say um in the finally so we have a try a catch and then finally no matter if the try block is successful or an error gets caught we can set is loading to false so no matter what happens the loading state will end and then also currently after typing a message and then submitting that message Jesus Christ this arrow is super annoying it won't be there in production but during development It is Well annoying we're mocking the call but you can see the focus is gone so if I were to want to type a message right away and spam my chat partner with messages I couldn't do that I would have to click into this field every time and therefore after the set input let's call the text area ref dot current dot Focus like that by doing that when I type a message click post the focus is right back and I can submit multiple messages great however for now let's get rid of the mocking and instead let's actually make the API call in the catch block we want to catch the Arrow by submitting a or by letting the user know via a toast notification it's going to be a toast dot error and inside of the error we can say something went wrong please try again later period and that's it that's all the error handling we're gonna do technically we don't even need access to the error like that and now we can work on the API endpoint this is probably one of the most crucial API endpoints we have at this whole application because it handles each message that is being sent to the user and for the API endpoint we're going to navigate into or Source folder then under app under API we are going to create a new folder on the same level as the friends are so in the API directory itself and call that some something along the lines of message and then inside of that message folder we can create another folder because remember folders only are represented in the URL structure and not the actual files that go into them and in this send folder we can have a route.ts as you already know great inside of this router TS all the Sending message logic is going to get handled via a post request so we're going to export an async function that is named post just like the HTTP method in all caps and just like that we can start working on the logic that this API Rod will handle everything is going to happen inside a try catch block so we can gracefully handle arrows let's start with the try we can close out of that and first off we need access to the body content that we pass in here if we take a quick peek at the chat input again we pass the text as the input and then the chat ID so we can destructure that in here we can say text and chat ID is going to be equal to the await rack dot oops Rec Dot Json not as John Json there we go and the rack is something we get right here as the parameter of type request we don't need to import that it's native and then we need to get the session in the Second Step called session because we require users to be logged in in order to be able to send a message this is going to be a weight get server session as always with the auth options in there and we can already do the first card Clause meaning if there is no session in that case we are going to return a new response with a message of unauthorized and a status code of 401 unauthorized great in the second step we want to get access to the user ID and the chat partner ID to verify that the user that is trying to send a message is one of these IDs therefore we can say cons user ID 1 and user id2 it's going to be equal to chat ID and because we know the structure it's a ID separated by two dashes or two hyphens and then the other idea we can split the chat ID at the two hyphens thereby getting two IDs we don't know which one is which yet but we can already verify that the authenticated user matches one of the user IDs in the chat ID we do that by saying if session dot you oops dot user dot ID is not user id1 and session dot user.id is not user id2 if it's not one of those in that case let's return a new response saying um unauthorized as well with a status code of just like above 401 unauthorized great next up we want to determine the friend ID based on the authenticated user's position in the chat idea so we just want to know who of these two IDs that we can't assign to either the logged in user or not which one of those is yours the logged in ones and which one belongs to the chat partner you can say Khan's friend ID is and then we'll do a logic check if the session.user.id is equal to user user ID 1 then in that case the friend ID will be the user id2 and else it will be the oops the user id1 okay so now we have found out what the friend ID is and by the way the chat ID and the text we could type those out directly text is going to be a string and shed ID it's also going to be a string just to get some typescript validation in this file and then we want to check if the friend ID that we just determined is in the user's friends list if it's not they shouldn't be able to send a message to find out if they are let's say const friend list is equal to a weight Fetch redis and then as for the command we want to get the S members and as for where we want to fetch them it's the user colon session dot user dot ID and then colon friends that that's the set we want to get all the members from and that is going to be a string array because these are going to be Json strings and then we can say and cons is friend is going to be equal to friend list dot includes it's an array method we can use and if that includes the friend ID and if these two people are not friends they shouldn't be able to exchange messages so if not is friend in that case we want to return just the same response as before unauthorized you should not be able to communicate with this person if you two are not friends great next up let's go let's get some information on the sender to then be able to display that to the user that receives the message for example as I showed you way in the beginning of the video in the demonstration when you're not in a certain chat window and you get a notification a message from someone it shows up at the top of the page as a toss notification and for that we need the sender information so cons sender is going to be a weight Fetch redis and then we want to fetch with a get request and we want to get the user colon at the session.user dot ID as user and actually let's verify this user this could also be a string so let's log it out for now I'm not quite sure of the type um so let's save this for now and we should be able to send wordful AI support a message hello and the hydration arrow is back at it again let's reload the page give it a few seconds to load correctly then the hydration error won't occur let's say hello click pause that's going to call the API route we got back if on 500 which is fine for now but hopefully we got the sender logged out and we did and we can verify okay this is a Json string this is not a user yet so we can cast it as a string and then say const parsed sender is going to be or let's let's just call this raw sender and let's just call this sender is going to be equal to Json dot parse and pass in the Raw sender and now we know the type it's of type user great so now we have access to the sender information and we've done all the necessary logic checks to determine that this message that's about to be sent is valid so all valid send the message now we actually want to send a message to the user to do that we are going to do two things first off we are going to notify the user that's receiving the message in real time and then secondly we want to persist that message in the database for now let's just persist the message and do the real-time features all together because I think that makes more sense in this tutorial or in in this video so um first off let's persist the data in the database you do that by saying DB Dot and then well we first need to import the DB like that and to actually persist the message we're going to say db.z at we're going to add to a sorted list that's what this means and the list is chat at the colon chat ID colon messages that's what we want to add and now as for the data that we want to add this is going to be an object that takes a a score that is going to be the timestamp and timestamp doesn't exist yet so let's say const timestamp oops timestamp is going to be equal to a date dot now they thought no that's going to be a number a Unix number determining the current time in JavaScript and then secondly as for the actual piece of data that we want to add the member this is going to be a Json Dot stringify message now what is this message it doesn't exist yet the message is going to be the following let's determine it up here first we need to say what the message data is going to be so what are we about to put into the database that is the message data and that is going to be of type message that we get from the validator like that this is going to be an object and inside of this object we can already see what we expect which is one the idea then secondly it's the sender ID oh and by the way the ID needs to be I don't think we have that installed yet do we no we don't so for the ID generation we are going to use a package called nanoid it's a super simple and super popular npm package that just lets us generate IDs let's npm install that npm install Nano ID hit install on that it's super lightweight and it just serves one purpose that being generating unique identifiers then as for the center ID we know that it's the logged end user you can say session.user dot ID and why doesn't that work because vs code was lagging okay then we want to pass the text we already know that exists and then secondly the timestamp great that's the message data now we want to parse that first cons message is going to be equal to and we know what to parse this with right we have the message valid data and here this is what we're going to parse the message with just to make sure everything is alright and for example if you wanted to limit the text um later on we're not going to do that but if you wanted to do that you would also pass a Max of 2000 that's why we're validating this beforehand um so we can say message validator dot parse and then pass in the message data so the reason we are validating is to enforce um custom properties if we wanted to I made this very easy for you to expand on I'm not going to check this but it would be a very common option to say something like there's a max length on the messages of 2000 characters for example and if you have a paid subscription like with Discord then you can send longer messages but I don't want to do that and just for this demo if you wanted to put this into production that would probably be a good idea that's why I implemented this great we're gonna do the real-time stuff later and now that is all we need to do to persist the message in the database let's return a new response saying okay quickly catch the arrow and then take a look at what this message looks like in the database first off let's handle the error if the error is an instance of error then we can return that error message and I need to type this correctly and we can return a new response saying error Dot message we know this message exists because we just checked if this is an instance of the error class then for the status we want to pass a 500 and if we can't catch the error like that if we don't know what exactly it is then let's return in your response saying in turn oops internal server error also with a status of 500. great that's the error handing done we are persisting messages in the database let's try out if it works let's read out the page oh and we also need to restart the Dev server in order to validate that let's read out the page and quickly see if we have done everything right or if I messed something up along the way which I hope I didn't do because if everything works now and this would load then it would be pretty simple adding the real-time functionality later on and this hydration error can go I'm not going to say that it can go away we don't want it here let's send let's send the message click post and see what happens so we sent the message we got a 200 response back that means that the message got persisted in our database right here we can see there's a new data type it's a chat messages and it's of type Zs which is a sorted list and we also see the data type SE which is just a set oh this is sorted set not sorted list and this is just a set which is unsorted but in the sorted set we can see it's sorted by the timestamp which we defined um right here as the score so we know the order of messages they were sent in that will make it super simple later on to display the messages in the right order and since we know the messages are working correctly let's try refreshing the page and there is the message how cool is that now the timestamp is still a bit off let's do that next let's format this into an actual time instead of the Unix timestamp but the message is working good progress if you're following along really really good we got the first message to work not real time yet but the message is there that's a huge step in the right direction let's log in with the other account and see if we can see that message let's log in with the support account if this loads hello there we go wordful AI support let's log in with that let's give this a hard refresh and it seems like we can't see the friends which is weird let's verify that we are friends with this user so what's the let's first find out the user idea so we need the user ID for support at wordful.ai and if I click refresh we can see 7b4 is the user ID let's see our friends and we are friends with the other user however we can't see that in this bar okay and I just noticed what the error might be it's that this says friend and not friends so I think we did little typo when we add or accept a friend I think it's rather when we accept it let's go back here and then there it is there is the error you can see right here when we add a friend we add it to the friends for one but to the friend for the other which obviously doesn't make sense so in this case let's quickly flush the database to flush all to maintain the Integrity of the data let's log out with this account that will take a second and then let's log in with both accounts again and try that again so currently we shouldn't have any data in our database if we refresh that great that works and now let's try in logging with one account then with the other and then sending a message and that should work so let's log out right away let's log in with the other account being the support account select that we are logged in let's add or friend admin at wordful.ai send the friend request that was successful now let's go ahead and log in with that other account again accept the request and then send a message and then hopefully which we would see the friend on both accounts let's click accept it's going to add the friend refresh the page and that is still there that's going to be removed later for now I'm going to refresh the page and now we can send a message to wordful AI support saying hello get rid of the hydration error and I think we need to reload the page for that to be fixed just send any message click post we can already see that message if we refresh the page however let's log out and that hydration error is super annoying honestly let's log out of this account log into the other one wait which one did we send it to I think this one let's go into the support account oh this was the account I sent it with so let's log out let's log out with the support account that received that message this one right here go into wordful AI admin and we can see the message is right there and wordful AI admin is also showing up as or friend really really good great work so far now let's quickly fix this weird timestamp that shouldn't be displayed like that instead we want to display it as the actual time of day that this was sent and to do that we're going to use one npm package a super handy little helper npm install called Date Dash FNS this will help us format the date into just a timestamp and we can be very precise on how exactly we want to format the state so once that installs correctly there we go let's start back up the development server go into or and we can close so many of these files I'm just going to do close all go back into our messages component and now where we are rendering out the timestamp where is that where are we rendering it out right here right down here um let's wrap this in a format timestamp function and pass it at the timestamp and then go ahead and Define that form a timestamp function right up here it's not going to be reused anywhere so we can just put it in line here and the const format timestamp is going to be taking the timestamp as a number and then it's going to return a format function that we now get from date FNS the package we have just installed we're going to pass this the timestamp and then as the second argument the format that we want is power s which in my case is going to be HH in uppercase and colon mm you can go back into the browser reload this page and now we should be able to see that the timestamp shows up correctly as the actual time of day that this message was sent there we go we can see 13 53 57 which is two minutes ago that works correctly you can do this with AM and PM if you wanted to date events gives you so many options to do that it's honestly great the message is formatted correctly really really good great work do you remember earlier when I said let's pivot for a second let's first finish the chat input so we can send messages and then let's finish the messages component that's what we're going to do now because the images are still missing on the left side and so this chat component is not quite complete but it's almost done we're missing two more things so we have the span element here then another closing span and then another closing div and under that closing div with two more to go then a parenthesis let's open up a new div with a class name that is going to be conditional so let's wrap this in the curly braces with a CN invoke that and by default is styles that we always want to apply are relative width of 6 and height of 6 and now we are implementing the images that we can show next to the messages as to which users send this message and then for the conditional bits of this class name let's apply an order of two if this is the current user and we want to apply an order of one if this is not the current user there we go and one more thing we want to apply is invisible if um this has the next message has next message from same user is what we called it then we're going to apply invisible to this image um which makes it still keep the space that it has so it's different from the hidden property where it's not shown on the page at all this takes up the space it normally would but it's not visible to the user that's the difference between invisible and hidden and then in here let's render out a next slash image that gets a fill property a source of the well when we are certain that this is the current user then this is going to get the session image which we still need to pass as a prop to this component which is going to be a URL so we can already cast this as a string or if this message does not come from the current user then this is going to be the chat partner dot image now both of these don't exist in our messages yet so we need to pass the session image and the chat partner as props which is going to be super straightforward because we already have that information in the parent component so first off let's add the session image as a string and then secondly the chat partner as a user we can receive those props in the component chat partner and session image and now if we take a look at the the file tree or whatever this is called we can see that the chat page.tsx is giving us an error it's complaining that we still need to pass the chat partner which is going to be the chat partner and then second oops not the chart partner and then secondly we also need to pass the session image which is going to be the session dot user Dot image there we go and I think the session image could also be null or undefined that's why this complaining rule or undefined okay now the arrow is gone let's go back in here we cast that as a string and now why is this complaining the chat partner does it not know what that is chat partner Dot image or because I'd call this charge partner chat partner this also needs to be chat partner that we're receiving and then that is also going to be the chat partner not the chart part called the chart partner in every spot okay it's the chat partner there we go okay great now this image is still missing in alt tag that we need to pass it it's mandatory the alt is going to be profile picture then the referrer policy for Google Images to show up correctly is going to be no dash referrer and then lastly this is going to get a class name of rounded Dash full so it looks good and that's the component done great we can see now there are images if we send just get away hydration error nobody nobody likes you we can send a message hit post and type another message right away those won't show up in real time yet but if we hard refresh the page we can see they are sent and then for the last image there is also the profile picture great but this is already looking really good and we're super far into the project now um let's see what we're gonna do next I think we are about to add the real-time features to really get this off the ground then add some route protection we've already done that we don't even need to do that anymore and we could add some loading States if we wanted to I'm not sure if we can yeah we can do that it won't it'll take like 10 minutes and it would really improve the app first off let's do the real-time features though and eventually we will also take care of the hydration error that shouldn't be there once we launch this to production sometimes it's not there but sometimes it is if you're really fast and clicking on the interactive elements then it is there and that wasn't like that in my um in the example I prepared in the project I already built but nevertheless it is here now and I'm gonna work to fix this I'm probably not going to show you all the debugging because you know that might take two minutes but that might take 10 minutes and we will also fix this Arrow at the end don't worry about it for now we're just gonna live with it and then later on we will fix it before we deploy this application first off let's get into the real-time features so what exactly is going to be real time in this application let's briefly talk about it first off when you receive a friend request we want that to be real time so when you're online and somebody sends you a friend request you should be able to see that pop up right here under the thread requests then secondly when you receive a message that should be real time and when you send the message the chat field should be real time and then the last real-time feature that we want is that whenever you receive a message and you're not in this message window then what happens a is a Notifier pops up right here saying you have an unread message and then secondly there's going to be a toast notification at the top of the page with the name of the person that wrote your message and the message content and their image right where you can click on it and then you'll be taken to this chat field of the person that sent you a message um let's decide on which real-time feature we will Implement first I think the coolest one is going to be the messages so we will do that last um first off we're going to do the friend request so whenever you receive a friend request there should be a little Notifier icon here popping up notifying you that you have in fact received a friend request okay let's set up the service that we are going to be using for this real-time communication and we will do that in the lib folder and let's create a new file in the lib folder called pusher.ts Pusher is a service we can use for real-time communication pusher.com this is not sponsored from pusher and what is this Windows Defender Firewall has blocked some features now we can we can allow access that's fine um so Pusher channels are web socket channels you can use that are scalable super fast and they make websocket connections especially in serverless apps like Nexus super easy and super intuitive I really like them they have a good Fleet here that is totally enough for our use case and this scales super well if you wanted to deploy this to production and but then that would cost you money in this case we won't need to pay because we won't be exceeding the free limits now to get started with Pusher that make super that make real-time communication super easy we're going to install two packets one for the server side and one for the client side so we're going to say npm install and then for the server side distribution of the real-time messages we're going to install Pusher and then for the client side receiving end of these real-time Communications we're going to install Pusher Dash JS so that's Pusher and pusher.js hit enter and that's going to install those two dependencies for us and that was really fast great we can start up back the dev server for now and let's set up Pusher in our lib folder in this first step to get started we need Pusher credentials and for that I'm going to go to Pusher click sign in and then we can sign in with Google for example I'm going to use my admin account for this and then we will be redirected and create a project and after creating a project it's going to give us some credentials we can use to access this programmatically so let's call this project Real Time Dash YouTube I'm going to host this in the European Union and click create app this process is super straightforward after creating that Pusher Channel we can go to app keys and it's going to give us everything that we need that we can copy and paste into our m.env file so keep these values in mind we can just copy and paste all for now go into our environment variables and then here we can use these values to initialize or push our channels later on so let's I'm just going to paste them in here so we don't have to go back and forth then let's declare The Pusher underscore underscore app underscore ID first this is what you can see up here the 1578 whatever let's put that in quotes there we go then for The Pusher app key that is going to be a public variable meaning we can also access that on the client that is fine to do that we can say next underscore public and that allows next JS to also access this variable on the client if we didn't include the next public and this wouldn't be accessible by the client at all because nexjs will assume this is a piece of very sensitive information that only can be accessed on the server we're going to call this next public pusher underscore app underscore key this is the key values you can see up here and then lastly for The Pusher underscore app underscore secret this is going to be what you can see right here as for the secret that's all we need we can get rid of the original stuff or dot env.local take a last look if you're still finishing this up and then we can go into our pusher.ds file and actually implement this so in or pusher.ts first off let's import the two libraries import Pusher server from Pusher and then import Pusher client from Pusher Dash JS one is for the server one is for the client as I mentioned let's work on the Pusher server first but before we do that I want to make it very clear to you why we are doing this let's quickly go to Excalibur down here and then the two things we're going to do is we have the service side right here and we have the client side over here and what we do is from the client side we can make a post request every time we send a message to the server containing the message content and then say or chat partner was down here so let's call this chat partner and DC the client right here is US right and this is our chat partner we send a message to the server that server puts it into the database for one into the sorted lists that we could see um right here in the sorted set excuse me not the sorted list this is where the server is going to maintain this message in database and it's also going to send this message out to anyone who is subscribed to this chat in real time using websockets so before even persisting this in the database it already sends that event out to the client and the like us and or chat partner so we can see the message in real time and to do that we need the server that we're gonna do right now and then the client to subscribe to any changes and so it receives the notifications from the server that's why we are doing what we're doing right now first let's export a const called Pusher server it's going to be a new Pusher server and that we're going to pass the app ID as the process.env dot Pusher underscore app underscore ID let's just pretend we know that exists and because we have defined it in the EnV file as for the key this is going to be the process.env.next underscore public underscore Pusher app key put an exclamation point at the end to tell typescript we know this value is set because otherwise this could be undefined in typescript would just complain the secret is going to be process.env Dot Pusher underscore app underscore secret and we also know that exists let's do a type assertion as well the cluster is going to be at least for me EU for you that could be us or wherever you put this push-up Channel and then lastly for the used TLS we're gonna say true um and I think that is encrypted data traffic the TLs that's what that is for then lastly for The Pusher client let's say export const Pusher client this is what we're going to use to subscribe to the events there's a new Pusher client in here as the app key the first parameter or argument the first argument we're going to pass the process.env dot next underscore public Pusher app here also tell typescript yes that in that in fact exists then passing object as the options which contains a cluster of also the same value we put above EU in my case and that's all we need to do now we set up Pusher and we're all ready to go to set up the real-time communication and as I said first what we're working on is the friend requests so whenever we receive a friend requests we are going to tell the client in real time that they have in fact received one to do that let's go in or friend requests component and you're going to be surprised at how easy this is so um let's do this right above here to subscribe to this event let's create a use effect call a function block inside the user fact and a dependency array just a standard use effect right and inside of this user effect let's use The Pusher client to subscribe right here to subscribe to any changes the server provides for this um for this user right this is in a user basis and we want to subscribe to the incoming friend requests we can do that by saying Pusher and then client because we are on the client side dot subscribe and we're going to subscribe to the template string of user colon then the session ID that we have access to and the incoming underscore friend underscore requests now one problem with Pusher is that you can't use colons in the Subscribe name that isn't considered an invalid character and that's why we need to create a little function that helps us um subscribe to this change by replacing the colon with something else let's do that in the utils um in the utos file in the lib folder and and here we're going to create a quick little helper const um called export const to Pusher key this receives a key as a string and then returns return the key dot uh the key dot replace and then here we're going to put a regular expression don't worry it's a very simple one we're gonna search for any colon globally in that string that's why we also put a g there so slash colon slash g and then for the second um parameter that we're going to pass or argument I think it's argument Jesus Christ it's a double underscore that's what we're going to replace it with Pusher knows what that is and we can declare this as a function and our function would also work doesn't matter very simple util function that we can then use to wrap this so I'm going to cut cut this value and in the Subscribe let's call to Pusher key pass in the value and that will bypass the invalid um colon as the character while still for us being readable in this format so we know what exactly we're subscribing to okay then let's bind something to happen whenever this function occurs right now we're just listening does this occur but we're not telling Pusher what to do when it actually occurs so we can say push or client because we're on the client side dot bind and we're saying whenever an event of this name right here happens right this is what we call separately on the back end so we on the back end we say trigger a function with this name right that's what we do on the server side that we then bind to a actual function on the front end in our case that's going to be called friend oops friend request Handler that function is going to handle whenever this event occurs now that function doesn't exist yet so let's quickly mock it for now friends request Handler is just going to be a function that does something for now it won't do much and then as for the return value we need to clean up after or self and we can do that by saying a push your client that unsubscribe from this so if we're subscribing we also need to unsubscribe we can just copy and paste this and change this to unsubscribe to this exact same thing and then secondly we can copy the bind and say we want to unbind this function from that event and can format this save it and now we effectively listen to any event that is called incoming friend request and we can handle that accordingly let's just log it out for now new friend request great so that means we are now successfully listening to that event on the client but we are never triggering that event on the server so that's what we will do next and the question is when do we want to trigger a friend request um event and the answer is whenever we send a friend request and we do that in our friends ad so when somebody adds someone as a friend that's when we want a real-time notification to go out and let's Implement that on the server side so for that we're going to use The Pusher server it's going to be pretty simple on the back end here before we persist the message in the database or the friend request rather we're going to notify all clients that this person has been added right and by clients I just mean the person that has been added that's what we're that's who we're gonna inform of this to do this we're going to say push a server because we're now on the server side dot trigger and then we want to trigger and top Pusher key obviously because we are listening to the same thing just so we can use the colons right here in the naming and in the template string we're going to say user then the ID to add because that's the user we want to notify and then the incoming underscore friend underscore requests and then for the event name right so this is the channel we're sending the message to that's what we're subscribing to right here on the front end and then to trigger this function that's what we pass as the second string in here the function we can just copy and paste from the friend requests so we don't make any mistakes it's going to be this function that we bind to we're going to put that in here the incoming friend requests and then lastly is the data that we want to send along in that request and I am getting some error I think that's because we haven't passed any data yet so for the sender ID we want to pass the session dot user dot ID and then for the sender email we want to pass the session dot user dot email and I did mess something up syntactically here so let's see what I did to Pusher key and okay so this should go outside of The Pusher key Pusher key should end right there so the first argument is to push your key user I need to add incoming friend requests second argument is incoming friend requests the actual function name that we're triggering this is the channel we're triggering it to and then third is the data that we send along in this request let's save that and see if it works correctly so whenever we send a friend request the other user should now be notified and a console log should happen saying new friend request to check if this actually works let me open a new browser tab also go to localhost slash login so I'm going to log in with another account and then let's first off delete the friendship between the two accounts I'm just going to flush the entire database for now so we don't have a friendship so I can add that user again because right now that wouldn't work because we have implemented some logic validation let me log out and let's open that in Incognito actually there we go so I'm not going to be logged in the data should be clear we can validate that by looking into our database it is let's log in with one account the wordful AI admin one then on my second screen here this is no secret and you can also watch me do this I'm gonna log in with my support at wordful dot Ai and then let me type in the password there we go I've logged in with my account now I have both accounts open into windows and now let me send a friend request to wordful AI admin press add and let's see what happens your friend request was sent and let's look into the console now because we expect this to say a friend request sent but it doesn't that's not how it should be let's look into this console window do we see anything and if we don't let's debug what happened okay so it didn't work let's go back into our database and delete the friend request so we can send it again let's delete that for now and that deletes all connection between these two and now let's go first into our server-side route where we are triggering this event saying trigger Pusher for example and let's see if we have any error aha we do have an error I'm saying can't resolve encoding um so that's that seems to be a module we still need to install let's install npmi encoding and I spelled that wrong and coding there we go install that I also did that in the other project yarn Dev let's start the server back up and let's just send another request before going into the debugging too heavily make this a bit smaller let's restart both browsers just to make sure we're on the latest on the same page with the latest code let that load for a second while the server reloads and then I'm going to send another request over and then let's see if it works so it finished loading let's enter admin at wordful.ai and we press add it says friend request send but we still don't get a notification okay so let's go into debugging I'm going to log trigger pusher and then I'm gonna log the subscription here subscribed to then a template string containing the same information right here and let's save all of that restart both browsers and also get rid of that friend request in the database right here let's delete the incoming friend requests and go into debugging okay there we could see it but now it's too late um let's restart the page it should be gone let's add this user again admin at workflow.ai press add the friend request was sent we still don't see it in the console let's see what happens in the console right here trigger Pusher that's good but we don't see that we subscribed to this event at all which is going to be the reason why this doesn't work so the subscription itself doesn't work that's why the function can't get called so there is an error with this right here I'm going to take a second to debug this and then let you know what the issue is so as it turns out there is no error at all it was just that we were navigated to the page that's why it never mounts it and so now we can see it actually subscribed to this event let's try this once more let's remove the friend request and quickly send that again from the second let's refresh the page send the friend request again and then we should be able to see that log in the console so admin Edward for that AI press add and when it says friend request sent we can see in real time without refreshing the page there is a new friend request really good and I hope I didn't sound too loud there in the microphone great so now we can see in real time that there is a new request and we can deal with it accordingly so we want to handle this event and the only thing we're going to do is add it to state so we can map over the friends that we're showing so instead of the console log that we currently have and I'm gonna move the debugging stuff here instead of the console log that we currently have let's put this away and let's set this into State set friends set friend requests now we need to know whatever they were previously and then to all the previous that we're going to spread in as the first thing in the array now we're going to add the current request which is going to be containing the sender ID and the sender email that we are sending along from the server right here as the data so we can also receive them on the client they're going to be passed in right here automatically into the friend request Handler so we can say sender ID and sender email is going to be of type incoming friend request that we have already defined previously because we were already in that file remember that's how it comes in handy now now we're going to put in the sender ID send your email and we are done that's all we need to do now we get access to this friend request in real time and I also want to show that little icon in real time as well and if we remember what this component was called it was the super long friend request sidebar options one let's navigate into that friend request Cipher option there we go and do the same thing we are going to listen to real-time changes in here and then update the state whenever they do occur to show it in real time to the users now as you know the drill bit no let's do it inside of a use effect and go back to the German keyboard for this instead of this use effect first thing we want to do is subscribe to this event to the same one by the way this is going to be the same exact event that we're listening to here so technically let's just copy the code it's just going to be simpler let's copy the whole use effect from the friend requests and exit out of all those files paste it in here now there are going to be minor differences but we still want to import the to Pusher key and then the difference is going to be in the friend request Handler we are not going to need any data in here because we are only keeping track of the amount of friend requests there are and as we know this is of type number so we can say set unseen request count to what oops to whatever it was previously and add one to that value and the rest is going to be the exact same so let's try that again let's delete the friend request we have in the database so we can send it again delete let's refresh the page so it should be gone it is great let's also refresh the page in here and now let's see what happens pay attention to the friend requests and ideally a little one icon should show up if I send a friend request let's send that over friend request send and it worked great so we see add a friend we see the friend request and in real time we can also see that a little one popped up here indicating that we have a new friend request really good now we can add the front and that shows the friend right here in the chats we could start chatting with that person that's one key real-time feature done now let's go into the messages and we want to implement being able to chat in real time as well right that's one of the coolest features of this whole application how do we do that that's why we created a separate messages component in which we can listen to the messages and also we have a state containing the initial messages so you kind of see a pattern here right we got the initial messages that are persistent in the data base from the server and then whenever some real-time changes occur we add that to the state to display it in real time that's how you do it that's the pattern I hope you have not by now so we can just copy the use effect we aren't going to listen to the same event but the main code is going to be similar so let's just copy and paste this to save some time let's import the use Effect The Pusher client Pusher key and then as for the logic in here we want to subscribe to a different event that event is going to be the chat colon chat ID that is a chat we want to subscribe to now the chat ID doesn't exist yet in this component so it's going to be the last prop that we're gonna pass in here I know it's quite a few props but in this case um it makes sense to pass this many just having and because then we can avoid fetching all that data again right here in the messages component which would be redundant then this is going to throw an error on the page because we're not passing that value let's quickly pass the chat ID the chat ID is going to be equal to chat ID now we have access to that in the messages oh and maybe that was a bit fast so in the messages component under the a dashboard and then the chat I just passed the chat ID let's do that go back into our component and now think about the logic that we want in here the event is going to be called differently I'm going to call it incoming Dash message and it's not going to be a friend request Handler but rather a message Handler you can just mark one then press Ctrl D to select the other ones and then change the naming of all of those together then lastly we want the unsubscribe event to be the same as the Subscribe event copy and paste it here and the unbind event to be the same as the bind event copy and paste it here great that's the naming done now the only thing we need to do is to handle the logic and this is super simple again we're going to set the messages get access to whatever they were previously I can close all of this and then put the message at the beginning of the array because due to the flex call reverse that I mentioned earlier everything is turned upside down so we want to add it to the beginning of the array instead of the end and then spread in all the previous values now the message we will get access to as passed to the message Handler from the server and this is going to be of type message great this comes from the validator right it does okay great so that's all the real-time logic we need to implement the messaging however the server side is still missing right whenever we send a message on the server side we still need to propagate that to this chat room so everyone who's subscribed to that um also knows there is a new message and the way we do that is we go into our route.ts where we send a message and then here we're going to propagate that to the clients so before persisting the message in the database like so you want to notify or connected chat room clients beforehand and we do that by calling The Pusher server dot trigger and now the event that we want to trigger is the one we subscribed to this one right here the chat colon chat colon and then the chat ID great and then remember we need to wrap this in a toe Pusher key because the colon is not allowed as a symbol then secondly is the event name that we want to trigger it's incoming Dash message and then lastly is the data that we want to pass into the function and this message right here is what's going to be received by this function right here that's how we get access to the data on the front end and that's all we need to do to trigger The Pusher notification for now later we will also add when you're not in the chat that you can see who wrote you on the top as a toast notification but this for now is what we need for the messages component let's go back in here let's click on the chat like so and let's say hello world click pause and see what happens so we can see the message and in real time we could see it pop up there on the left side let's try another message how cool is that hmm hello world how are you Head Post and we can see it right there on the other client in real time and if you want to dive deeper into how this works we can go into the inspect element under the network tab like so and we can see the web sockets that we subscribe to Let's reload this page and you will see something pop up now which is this name right here this is not a name that we came up with the 4e56 whatever whatever and this is generated by Pusher we can click on that and then under the messages tab we can see the current web sockets that we are connected to and when I send a message pay attention to what happens right here you can zoom in a bit so it's easier for you to see it's going to be right here pay attention a new event happens the up Arrow stands for an upload the down arrow stands for download so we are downloading our own message but the same thing goes for this client right here when I navigate to the network tab whenever we send a message this person also gets access to that message via the same web sockets so if I go back to this put it right here in a small then you should also be able to see this message message pop up right here post and you can see the download event which is what we're displaying for the message right here that's how the real-time communication Works Pusher makes it super simple I really enjoy this process it's great it's really really good one last thing we could submit an empty message that's not really what we want so let's quickly fix that in the messages right here and by the way why is the sender why are we not using that that's uh that's kind of weird right oh that's what we're going to use to display the toast notification later if we're not in the chat and we get a message okay that's that's totally fine that we don't even know that's how it should be now before we submit a message let's go quickly into the chat input and to one little logic check here if there is no input then we're going to return so we're in the chat input component under the send message function that we have to find if no input return what that does is when I type in nothing and click post it doesn't work whereas before we could send an empty message which is not great do it good work really really good we have one of the core features done which is the real-time messaging we're making great progress on this one so quick summary what we've done so far we've created the whole UI really nicely we can add a friend we can see friend requests that we have received in real time we can see all the chats that we have we can chat in real time with the data being persisted by redis and the main advantage of redis is it's super fast which makes sense when we're using a real-time chat application really good okay one more thing that we want is we want to be notified when we're not inside of a chat window that somebody has wrought us a message and we also want to implement some minor stiding so for example applying some padding in the layout which is going to be a super simple fix in fact we can actually do that right now so wherever we render the children let's quickly fix that let's go into our layout under the dashboard scroll all the way down to where we render all the children right here and let's quickly wrap them so this looks a bit better let's wrap them in an HTML site tag render all the children Inside So currently we are in the layout for the dashboard and then almost at the bottom of the page with one closing div and this aside is going to get a class name of Max height screen oh by the way if you're wondering where we are the file tree it's right here dashboard dashboard and the layout component right here this is going to get a Max height of screen container padding y of 16 on medium devices and up a padding y of 12 and also a width of 4. let's save that and if we reload the page we're gonna see if this looks better it's going to be a bit more in the center takes a while to reload let's give it a hot second and that's not the what should have happened should it I think we didn't Define the container yet is that possible let's go into the Tailwind dot config and yeah we haven't okay that's why this doesn't look as good so to fix that let's navigate inside our tailwind.config.js under the theme we get a container property we're going to expand on that this is going to be Center oops Center true we want a padding off in string 1.5 Ram and then for the screens we want to pass the 2XL and that gets a string value of 1360 pixels now let me quickly check if we have defined the okay and the second thing we want to do is give this container a bit of custom styling in the globals.css really quick navigate there and then we want to apply a custom Tailwind style and the way we do that is by saying add layer base then we can pass in or custom property like dot container the class we want to apply this to and then what we want to apply with an add apply and now simply the class names that we want to give this so I'm going to say a Max width of 7xl mx-auto padding x04 and small a padding X of 6 and then on large devices end up a padding X of 8. so max with 7xl MX Auto panning xf4 on small devices end up a padding X of 6 and then on large device snap a padding X of 8. save that and now let's take a look at our component again and I think I messed up some syntax in the Tailwind config because that is not type checked did I let's let this reload yeah I probably did let's go back into the Tailwind config and what did I mess up [Music] um let's take a look at this there needs to be comma there and the comma there okay so this needs to become a separated let's go back we got the page and hopefully now that that still doesn't fix the error okay let's go into our globals wait let's compare the turbine config so Center true padding you have a comma there no that should be the issue let's go into our globals maybe this dot is the problem delete that go back in here reload this page and oh Max 7xl is not a class this needs to be Max with of 7xl that's what the issue was great let's reveal the page and now it should work and we can see there is padding applied now this looks a bit better just looks a bit cleaner you know as I said I'm going to still debug the hydration error so it won't be there later and now the padding is also applied to the chat great okay we've added the messaging functionality successfully very very good however when we are not on the page currently for example let's go to the friend requests page wait for that to load and then when we get a message in the chat the message does come through successfully and we can see it when we go to the chat right if we load that we can see the message um and we can see the hydration error however the message does not like we don't get notified of that message and that's not ideal we probably want to fix that so what I thought of is how about we show a toast notification at the top of the page and a little icon next to the next to the front for the corresponding chat that notifies us of the incoming message I think that would be a good approach to do this so the first step to implementing that would be to somehow listen to the event of an incoming message and we need to do that on a component that is always mounted when we get a message right because only components that are mounted can actually listen to the real-time changes and ideally um we go for the sidebar chat list component because this component right here if I make this a BG red 500 you can see exactly where this component is let's read out the page you can see this is the component it's red it's the chat right for each chat and it makes sense to do it in this component because when this is visible we know we can listen to the event because you know it's visible it just makes sense to do it in this component so we can close out of most of the others let's close all the saved ones and then inside of this component let's listen to the same event of an incoming message in this component so we can then display it to the user as an unseen message as we have already prepared up here so let's create a use effect and this is going to be the last real-time feature of the app the chat and the friend requests already work so in here we know the drill let's get go for the pusherclient dot subscribe and the event we're going to subscribe to will be converted to a pusher key to get around the invalid characters and then inside of here we're going to pass a template string and then the user session ID and then the chats this user has okay so for any chat the current logged in user has we are going to listen to messages in that chat or in all of those chats and also we are going to subscribe pusherclient.subscribe to all of this person's um friend request so we're subscribing to anyone adding this person as a new friend and same thing here to push your key and then a template string the user at the session ID and then be friends instead of the chats so those are the two events we are listening to and then let's just um clean up after ourselves right away so we don't forget that we can just copy this and turn this into an unsubscribe so we're unsubscribing from both events and then let's bind these subscriptions to certain functions because right now we are listening to the events but nothing is happening once these actually get triggered so for the new message um in the chat we can say pusherclient dot bind again we are binding an event to a certain function the event is going to be called new underscore message and then as for the function that's going to handle that we are going to create a chat Handler and then similarly we want to bind one more function that is for the new friend event we're going to call it new underscore friend and just like with the chat Handler this is going to be called a new friend Handler great now these two don't exist yet for the chat Handler let's create a function however the new friend Handler is a bit simpler let's start with that the only thing the new friend Handler does is refresh the page because when we get a new friend we accept someone as a friend the page should be reloaded and doing that in next.js is super simple we've already imported the router in here so we can call the router dot refresh and that's all we need to do to refresh the current window without hard reloading the page and then as for the chat Handler what should happen when there's a new chat message let's just log something out for now cons chat Handler is going to be a function and then here let's console log new chat message okay now as for these events right let's go into our route.s to determine which of these were already triggering so we're subscribing to the chats and the friends and if we go to something like the message Central as we can see for this event right here that we're triggering on the server side it's only for one certain chat right we we don't want to listen to All chats we don't want like a hundred websocket instances if we have 100 chats instead what we can do is trigger a chat event and that contains the message information and by that we know which chat this is concerning and also we only haven't we only have to listen to one event in that case which is way more convenient so let's do one more trigger from The Pusher server dot trigger and that will be for any message that this user receives in any chat so that we can then display that on the front end this will also be a two Pusher key to avoid any invalid characters and we are going to say in here the user colon then the friend ID and then the colon chats because that's what we're listening to on the front end and then as for the event name that we want to trigger it's a new underscore message and I just noticed we have a hyphen here and an underscore here and doesn't really matter for now and then for the data that we want to pass it's going to be all of the message and then we want to extend on that with the sender image and the sender name that we can then display on the client in the tools notification that shows up first off let's spread in the message as is so if you don't know what the spread does it means we are taking all the properties of the previous message putting them in here so it's like the ID the sender ID the text and the timestamp are in here now and then for the sender image we're going to choose the sender dot image that's the reason we fetched the sender earlier and then as for the sender name this is going to be the center dot name pretty straightforward there needs to be comma there we can save the route and now on every message that we send for the corresponding chats for the user we are emitting a new message event and that event is going to be triggered on the client that receives the message so if we go into the browser and then tap into the network tab for example in this browser right here let's just restart the page really quick go into our console um where is that up here into the console let's clear the console for now okay I moved this um to my other screen just for a second and now when I send a message click post let's see what happens so the message is pulsing we received the message here and then let's look into our console it says new chat message so if you go and check and go into our use effect we can see the subscription worked successfully and we subscribed to the chat message coming in however right now we don't have access to any of the data that comes in and we want access to the message properties but also the sender image and the sender name in here and to do that we need access to the message that we get right here but this is not just of type message right if it were then we wouldn't have access to the sender name and sender image so this is kind of like an extended version of the message and to reflect that we are going to create a type up here in the sidebar chat list if you're in typescript interface extended message to reflect that and the extended message extends the normal message meaning we copy all the values that we have in the normal message inside the extended message and then further we know there are these two properties right here the sender image and sender name we can just copy them from oral.ts paste them right here in the extended message and then both of these will be of type string great uh Miss and this is going to be the extended message type that we have just created so now if we do the same again but now log out the message in here let me open up this tab and this tab send a message hello hit post on my second screen here we can see the message coming in and then there's a new chat message and we have access to all the important stuff that we need to display the toast notification to the user suggesting that there's a new message um coming in okay now to display this message um first we want to determine if the user should even be notified if I am in this chat right here then I should not be notified via a toast notification that I got a new message right I'm already in the chat there's no point in doing that only when when I'm on some other random page in another chat then I would probably want to be notified of that message and to achieve that first let's have a constant called should notify and this is going to be equal to and that's why we imported the path name the path name is not equal to a template string that says slash dashboard slash chat slash and then in here we are going to use our chat href Constructor that we have created as a utility function and pass it the session ID and also the message dot sender ID because that's the other missing piece of the chat ID right so we are constructing the message and we should only be notified if we are not in that particular chat already and if we should not be notified if should if not should notify we are going to return early as a guard Clause meaning the code below will not get executed and then down here in that case should be notified and to be notified we are going to create a custom component and the toast Library we're using allows us to do something cool we can import the toast and then give it a custom and then here we can give it a component to render a custom toast notification we get access to a t the toast value as the first parameter and then we can render out or custom component that will go right here where I just commented that in however that doesn't exist yet so for the custom toast notification that is going to be a component I'm just going to call unseen chat toast let's do that in the components create a new file called unseen chat toast.tsx you could make this more reusable if you wanted to if you wanted the same toast notification for different purposes in my case I just want it for the Unseen unseen Shadows that's why it's so bound to the name instead of just a generic extended tools notification making that more reusable it's not necessary in our case that was that would just add some unnecessary overhead as for the component itself and here we are going to create a div with a class name oops with a class name there we go that is going to be conditional so we're going to put it in the curly braces and import or handy CN helper function and now the question is oh CN and the CN will get a Max width of medium by default a width of 4 a BG of white a shadow of large rounded Dash large let me close out of that a pointer Dash events Dash Auto then a flex a ring dash one and then a ring of black ring Dash black and lastly a ring Dash opacity Dash 5. that's what we always want to have and then as for the conditionals if we have well first we need access to the T right if you take a look at this we had access to the custom tools notification where we can see if the toast is visible and so on we need access to that in this unseen chat host so we are going to receive that as a prop right here and this T is going to be of type toast which is a type we can import from react hot toast it's important this is going to be just the type that we need to import and now for the conditional class name we can say if the t is visible then we want to display an animate Dash enter and we only want to display that is if T dot visible and then let's put that below here and animate Dash leave whenever not t Dot visible there we go okay that's for the conditional classes and now if we press whatever is on the toast notification we want to dismiss it as well in the div let's put an anchor tag and this anchor tag is going to get a dynamic href so whenever we click the total notification we want to be put into the chat where we got this message from right and the reason we're using an a tag again is to force a hard Refresh on the messages so we never get stale data if we didn't do that and just use the regular link component the regular link component then we would get stale messages and have to reload the page manually to get the latest data which is not ideal you know so in here we want to pass the slash dashboard slash chat then as for the Dynamic stuff we want in here it's the chat href Constructor as we already know and pass at this session ID and also the sender ID both of which we also need to receive as props session ID of type string and sender ID of type string as well import or accept both as actual properties in this component and that's it then for the on click Handler whenever we click this toast notification we also want to get rid of the toast to do that we can invoke an inline function called toast that we get from react.host dot dismiss and then here we're going to pass the T dot ID to dismiss and lastly for a bit of styling of this anchor tag it's going to get a class name of flex-1 and I and a width of zero and a padding of four so Flex one with zero P4 that's it for the anchor tag inside of here we're going to render out a div with a class name of flex and items Dash start oops item stash start inside of this div goes one more div with a class name of flex shrips shrink zero and also pt-0.5 give it a little bit of top padding and then one last div goes in that div with a class name of relative because we're going to render an image inside of this height of 10 width of 10 and here we're going to render out the profile picture of the person that sent us the message to do that we're going to import the next slash image which is automatically optimized give it a fill value a referral policy of no dash referrer because we're going to render a Google image in here a class name of rounded Dash full then a source of the sender image which we don't have access to yet so same drill accept it as a prop and import it as a string type and just for accessibility purpose we also want to accept the sender name so we can use that in the alt tag so impaired people like visually impaired can also know what the image is supposed to mean and we also need to accept that sender name into the component and a lot of properties unusually many for a reusable component as I mentioned earlier reusability was not the focus with this component you could easily turn it into one by abstracting it a bit that's not necessary for us in this case though so for the alt attack in this case we can put a template string and then in here goes the sender name and then profile picture so everybody knows what this image is supposed to mean then after the closing image tag one div down two diff Downs create a new um and one anchor tagged on wait now one diff down two div down space space so we have a closing div a closing Ang tag and a closing diff below this and right here we're going to create a div and this is to display the sender name and the sender message inside of it we're going to give this a class name of margin left three just space it a bit from the image a flex of one and then here go two P tags first one containing the sender name with a class name of text Dash small font Dash medium and a text Gray of 900 and then we can create a second P tag right below that that renders out the sender send oops that goes into curly braces of course oh and I switch to the English keyboard that's why it doesn't work this will contain the sender message and this p-tech is gonna get a class name of margin top 1 text Dash small and a text Gray of 500. now the sender message is the last prop we're gonna accept and as I said unusually many props essentially turning this component into something that is not reusable but that's fine for us so we are going to render out the sender message right here and one quick aside on that you don't need to turn any like every component into something super usable and my personal philosophy in react I've acquired over the time copy and pasting code is fine if you do it once or twice if you do it more than two times you should definitely think about turning this into a reusable component copy and pasting code once or twice just saves you time there's no need to add the overhead for yourself to turn everything into a super usable component if you're not certain you're actually gonna need it so just a quick aside on my personal react philosophy in that sense okay after the two P tags one closing div two closing div one closing a tag one closing diff left but before that last closing div space space and create one last div with a class name of flex border Dash l and a border Dash gray-200 inside of here goes the button that we use to close this toast notification render out a button where with an on click Handler of um toast dot dismiss once again and here we're going to pass the T dot ID so react hot toast knows which toast we want to dismiss and this button gets a class name of with full border border Dash transparent rounded Dash none rounded Dash right dash large so just the right side is rounded and then we also want to apply a padding of four Flex items Dash Center justify Dash Center a text of small a font Dash medium a text Dash in the indigo-600 on Hover we want a text Dash Indigo Dash 500 just turn this a little bit lighter and then only a couple more classes left what did I do oh okay so after the hover text Indigo 500 goes a focus pseudo selector saying outline Dash none then on Focus we want a ring Dash 2 and last class on Focus we want a ring Dash Indigo Dash 500. I promise that's all the classes there are and this component and then inside of this button we'll put a closed text great so that means we can now use this unseen chat toast to render it out whenever we get a new notification and we only want to notify the user when it's in a chat they are not in we've already done the logic for that so what we only need to do now is pass the Unseen chat toss right in here it's going to be self-closing and expects a lot of props first one being the T second one the session ID so session ID is equal to session ID the sender ID is going to be equal to the message dot sender ID the sender image IMG is going to be equal to the message dot sender image the sender the sender message is going to be equal to the message dot text and then lastly the sender name is going to be equal to the message dot sender name now you might be wondering Josh why didn't we just pass in the message as is yes you could have done that and again reusability is not a huge Focus however I feel like it makes more sense to pass each property separately because that's what you would also do if you wanted to turn this into a more usable component if you just pass the message and here this component would inherently be bound to the message however you could still change this text any more recomposable fashion by passing each property separately from wherever you render the component instead of instead of the component itself so we're stripping kind of the two close together linked logic away from this bypassing this many properties into it and then lastly after showing the custom toes we want to remove this from The Unseen messages so we're going to say set unseen messages get access to what they were previously and then we'll put the previous and then um the message so uh yeah we're pushing it into the Unseen messages right so when we when this function gets called we are notified by the server that there is a new message and we want to add to the Unseen messages not take anything away that's why we're pushing it into the array now this gets really you you'll know what I mean when we just try this out the logic is done so we can try this out again I've got the and what the hell was that loading we're going to work on the loading later by the way don't worry about that um I've got the second um instance right here open on my right screen just so you're not confused and if I'm in this page right here let's reload this and I'm going to send a new message saying something like hello click post and let's see what happens yes we get the toast notification wordful AI support hello and a new message popping up right here nice let's try that again hello too let me click post here and as you can see there are two unseen notifications and we get the message if we click this it's going to take us to the chat and we can see all the messages and The Trusted hydration error that's gonna go away in production but we can see the message and if I type a message while we are in this chat we can see the message shows up normally but no toast notification and that's because of this um guard class we have in place right here to determine if the user should be notified or not so this works beautifully just send another message click post it will show up here in a second and it didn't oh and one more thing I think if we type a message right now okay that shows but there might be a bug because right so what we want to do one thing that we need to add is that the user fact currently does not listen to path name changes so if I were to go into the chat and then reload the page inside of this chat and then navigate away from it the user Factor wouldn't know about that so if I were to go into my second account now if the page navigation finally loaded it can take a long time sometimes if we're not in the Cur and the in the actual build let's give it a hot second to load so what I mean is if we're in the chat and then navigate away um to another page for example the add friend now the use effect probably wouldn't know about that if I send a message right now here on my second screen the message is sent as you can see if I move this over but there is no toast notification on the other client and that is because the user Factor right here is not listening to any changes um From the Path name so we are missing dependencies that we need to pass in here those being the path name then the session ID basically everything inside the user fact where we get the values from outside of the user fact and also the router so if we try this now and if I and the loading just looks god-awful holy um if I navigate it to this page reload and then navigate it away from it now the use effect has listened to that change if I send a message again this should not work and it does so we fixed that bug great now we get the message if we click on it the icon will go away the toast notification will be dismissed and if we land right in the chat where we can just write real time messages how cool is that super cool very very nice so we've got the message functionality implemented the toast notifications implemented I think we're almost done I mean the videos probably long enough I don't know how long it currently is but it's it's taken me a long time to record this I hope not longer than like six hours or something um we've done the road protection last thing would be to add loading States and that's a very enjoyable step actually then we can build it out deploy it and that would pretty much be the whole project done oh this was really quick okay um the the loading states are pretty enjoyable to do we can start so what the loading states are what I'm talking about here is when we click on add friend for example you notice there's a slight delay between clicking it and navigating to the actual page in production that delay is going to be way shorter than in development however there is still going to be a very tiny delay and just to serve the users a better experience while it loads we can already render the page but just in a loading state with some skeleton page just rendering so the user knows okay um this is currently loading and the way we do that in Nexus 13 is actually super enjoyable to do so for example let's start with the add page let's go into our source directory the app and then enter the dashboard click on ADD and then let's add a new file called loading.tsx in next.js this is what Nexus falls back to when the component is loading which is super helpful we can just render out a functional component in here and as for the loading set itself we are going to install a package for that called react loading skeleton it's going to help us achieve this very neat very clean professional looking loading effect and we can copy the npm installation from their npm page it's called react Dash loading skeleton navigate back into our project and what is that how to perform a a full refresh that's fine we don't worry about that and then npm installed the react loading skeleton great after installing that we can start the dev server backup exit out of that and now for this loading State let's have a class name with a width of full Flex then a flex Dash call and a gap of three this is going to be a super simple component by the way don't worry then we want to render out a skeleton that we get from react loading skeleton and then we also want to import the CSS now to get their CSS we are going to copy this line right here import react loading skeleton dist slash skeleton.css we can just copy that line paste it in here and so the siding is going to be automatically done for us the skeleton is going to be self-closing with a class name oops that needs to go in front of there with a class name of margin bottom-4 then a height of 60 and a width of 500 to Mark the large heading that we have so we are mocking and did I start the dev server back up I hope I did yes I did so that should be loading in development that will always take a while so we're marking this um go away we're marking this big heading right here then under that skeleton we're gonna render out another skeleton with a height of 20 and a width of 150 that's going to be self-closing we can just copy and paste that by pressing shift alt and arrow down this height is going to be 50 and the width is going to be 400. great we can save that and now to really show you what this does let me insert a custom loading behavior on this page you don't need to copy this code don't worry about it and but I'm gonna await a new promise that gets a resolve function and then set a timeout where we call the resolve after 5000 seconds let's say this is going to be this is going to make the page very slow to load forcing the loading say that you can see right now right so this is the loading state that we're marking where the input is the heading just as a loading page for a better user experience let me remove the loading we don't want the actual users to load for that long let's hit save on this and then we can pretty much just copy the same loading State over to the requests let's just go on here copy the loading page and create a file under the requests TSX file we can just paste this file we're going to change the skeleton styling slightly so first one is going to stay the same second one is going to get a height of 50. and a width of 350 we're going to remove the third one that is currently there so we only have two and then we're going to copy the second one three times so in total we have four skeletons three of which with a height of 50 and width of 350 each one mocking one friend request that we have let's check out the page navigate to friend requests and there we could see the loading State just for a second I'm gonna not mock the delay you know what we're doing here I just showed you that and then lastly we're just going to take just a little bit more effort is the loading set for the actual chat um page so to do that what we can do is um well it would probably be easier so how I did this in my local development was I went into each component and then yeah actually let's just do that um so we're gonna navigate into the chat file it's going to be called page.tsx under the chats and then we're gonna copy all of the code that is in here and paste that into a new file called loading.tsx that goes into our Dynamic shed ID Road let's create a new functional component for this and just just paste in the code for now don't worry about any errors now we're going to go into the messages component and do a similar thing messages copy all the jsx that is in here just copy and paste it and place it instead of the messages then lastly we're going to do the same for the chat input chat input there is going to be a lot of cleanup involved in this this might take one or two minutes um but then we have a clean nice loading set for this and replace the chat input with the same M code I just want to show you how I normally do this for my apps and so there is a lot of dynamic code involved right now but we are going to replace everything that deserves well actually let's just copy this from the GitHub repository there's no that there is really no point in just typing this out there really isn't um but for for a quick note on how I normally do this I copy all the jsx and then replace everything that needs Dynamic values like the images the chat partner name everything that only that has to load its value right everything like that will be replaced with a skeleton and all the other styling that is involved stays normal right we we have access to that while it loads and all the dynamic values like the text area and so on are going to be replaced by a skeleton now this would take just too long and it really is just kind of stupid work let's navigate into the GitHub repository I've opened it here on my um right screen currently it's still private but of course I'm going to make it public for the video then let's navigate into the Source directory under app and then dashboard and the dynamic chat route go into the loading dot TSX copy all the jsx that is in here there's still some comment out code that's fine just copy the whole thing and that's going to be fixed when the video goes live and then replace this whole component with the loading State now we still need to import these skeletons from react loading skeleton and that is going to be a default import skeleton from react Dash loading Dash skeleton there we go and that fixes all the issues as you can see this is the reason I didn't want you to just write this out it doesn't make sense but I did show you the process on how I actually do this just copy all the jsx and only replace the dynamic values we can save this loading.tsx if you even want to have it in there it is honestly just a preference thing I think it does make for a bit of a better user experience and then when we navigate to the page in production it's going to be a bit easier to see while it loads the page the loading.tsx will be shown to the user great okay um I think that is the build that is everything done right let's go through this together so we've done the project setup of course database integration authentication sending friend request Works seeing friend request works also in real time accepting works also in real time I'm chatting with database persistence in real time we've done everything let's go through this from start to finish but before let's run a lint to see if we have any errors in here that we should fix first and so if the linters error then you know you couldn't Pro um you couldn't at the moment deploy the project because that requires a successful link um okay so first error we have under the login page is that we are calling the use state in a component that is not a react component and the reason react thinks that is because this page is lowercase while being a client component that's the problem so we're going to navigate to our login page type of the page as a large piece and also we can remove this empty object pattern because we are not receiving any props in this page um anyways and that should fix the lint Arrow right here react use effect has a missing dependency under friend request let's hold shift and click that so we can go to that particular bit of code and copy the session ID and paste it right into our dependency array right here well we didn't paste it but let's just put it in here session ID there we go so that link will be fixed this one has a missing dependency chat ID in the messages let's navigate to our messages component to the use effect and why is messages being marked as a missing dependency is it in this line oh it's chat ID marked as a missing dependency okay that makes sense because we're using it inside of the user fact as I mentioned earlier you need the dependencies for everything that come from outside and are being used inside of the use effect in this case the chat ID counts as that okay so that should be all the linting we had arrows and warnings on fixed let's run the lint Again by running yarnet or npm run lint hitting enter and that's going to scan our entire project for any more errors now there's one more error and that is under the friend request sidebar options let's go into that component and it says the session ID is missing as a dependency so let's put it in there hit save and now we should really be good to go let's run the linter once more and hopefully we will see no es lens warnings or errors great if you still see any um it will tell you what to do to fix those errors and if you can't manage to do it obviously there's the GitHub repository if you need to take a look at that okay let's build out the project yarn build or um actually let's let's just use npm for all all of this to make this more beginner friendly npm run build type that in and that is going to produce a production ready project of the code we have written for development that we can then use to a tester application and secondly to deploy it to the web to versel um to the Internet so people could actually use it Okay this may take a while this is not a super small project it's not not huge either so it will probably take like half a minute to a minute it's already building the pages which means it's almost done and then we can run npm run start to start up the production version of our project the one that would be actually um represented in production for a final time let's clear our database and see if everything goes to plan so right now all the data is clear there is no data in the database let's log out from the database and it Arrow there um probably the no it didn't okay we don't have an error in the console that's good okay so I've got both open right here um the logo you could replace with anything you want um and then or you could copy the logo I have put in the GitHub repository if you wanted to I'm going to log in with my account right here the wordful AI admin one and then on the second one I'm going to log in with the um with the support one and I'm still logged in here that is not ideal oh because I'm already logged in in this browser okay so that should still have created the data yes it did great so that's just very convenient that Google automatically logs Us in that just knows this dashboard is still incomplete we still want to show the recent chats in there but that's not rocket science and we can do that in five five minutes I think um now first let's test if everything works correctly let's put this into a split screen and then let's add a friend the other account press add we see that come in in real time on the second account if we add this person as a friend and navigate to the actual chat we can see that this person is now a friend however this friend is not added in real time to this browser that is something we still need to fix only if I refresh the page that would happen that is not ideal you probably want that to happen in real time and then if I send a message that message gets propagated in real time to the other instance to the other client which is amazing that if I navigate away from that page and we send a message we see that pop up in real time as well beautiful we can close the toast if we wanted to sh is right here if we click it we can see the message hello world show up really really good great work so two more things that are still missing if we navigate to the home page we want to see the um the the recent chats and then also when we add someone as a friend on one client we want that to be happen in real time on the other client as well and we can do that by forcing a route to refresh on the other client we could also keep the friends in state and then trigger a real-time event now for the two things that we wanted to fix the first one being that when we accept a friend in this account then the other one should be refreshed in real time as well to reflect that friend adding change and that is not going to be rocket science because um we are already listening to that exact event in the sidebar chat list so when we take a look at this code we are subscribing in the first place to the friends and then secondly we are binding the new friend function meaning if we fire an event for the friends channel in which we call the new friend function then the Handler for it the new friend Handler is going to be is going to get called and refresh the page for the friend request to show up and to call this however we need to fire the event and if we think about where to fire that event that would go into the acceptance of a friend if I accept you as a friend then your browser should be refreshed if you're logged in if you're not logged in if you're offline then it will be reflected and once you started the app anyways right we don't need to worry about that case so to do that let's go into route.ts for the acceptance of a friend and right here we can get rid of this line by the way that we calmed it out right here before the has friend request or after that and then before adding to the database this is where we're going to trigger or push a function because by this point we have validated that everything is fine and the user is allowed to accept the friend to trigger the notification let's say notify added user of this change if they are online it's called The Pusher server nope not The Pusher client pusher server there we go dot trigger that's how we broadcast an event to all clients as you probably know by now for the channel we want to pass the Top push or a key and then as a template string the user for the ID to add because we want to notify the other user not the one that is currently logged in Colon friends friends there we go and then as for the second thing for the event name that we want to trigger it's going to be the new underscore friend that's what we're listening to right here with a bind it's a new underscore friend that's what we're triggering right here on the back end and as for the data that we want to pass we don't really need to pass anything we can just pass an empty string an empty object if you wanted to and because we don't need to receive any data for this new friend Handler to be run you could also do this with state right so it would be an easy thing to just add the actual friend because you already have the um date so no we don't but we could get the data because we have the ID that we want to add so it would be easy to actually fetch the friend data from the database pass it along and then add that friend to State instead of refreshing the router as I mentioned earlier that would also totally be possible um if you're concerned about this um you know removing someone's message rather typing for example first right now this is totally um enough okay and I think that's all we need to do let's try this again let's go back into our browser clear or database where the CLI from upstage makes it super simple clear the database we got an OK meaning that the database deletion call was successful let's log out of our accounts and for now let's rebuild the terminate patch job yes let's rebuild the application so we can test it while it was is also very fast because when we build we're using the production version meaning everything is way faster than in development because in development all the functions and so on get run every time you know we navigate to a new page for example that's why it's super slow in production but so fast in well super slow in development but so fast in production let's run npm Run start there we go let's start this party back up let's login and here I'm going to move this into a split screen where we log on to the admin account in one and the support in the other that has created all the values in our database great and now if I go to add friend here add the support email press that first off the friend request pops up in real time and then when I accept it here this browser should also be refreshed let's press add and it is the browser is refreshed now you could also refresh the browser for this notification to go away but that will go away anyways and when I click on something like the admin account and then we can type happily along and that message gets shown twice which is weird and I think I know why that happens I just looked into the code and so right here where we bind the functions we are not unbinding them so we get a memory leak in that sense where the function run multiple times and when they are called because multiple instances are bound to this event which is not ideal so let's copy the binders paste them in here and instead of binds we're just going to put unbind to prevent any memory leaks from happening let's save that now because we're in the and built we need to rebuild this for the color changes to show up and then we can test that whole thing again now we know this will build correctly because we've already done the whole lending step that we need to deploy which is going to save us a lot of time and deployment because then we know it will deploy correctly let's start this back up by running npm Run start or here and start and then let's try this again let's refresh this refresh this and now hopefully only one toast should oops should show up hit pause there we go and only one tells shows up if I type another message and one shows up for that great that's exactly what we want we get access to the messages can answer them everything works as expected really really good so the last two things before we are totally done with this project is one building the dashboard to show the most recent messages and your most recent chats and then secondly the mobile layout for the sidebar with a beautiful slide over that we had and those are pretty simple things to do so most of the work is already done let's get started with the dashboard first let's build that out to do that we're going to navigate to the page.tsx for the dashboard currently we just show dashboard here which is of course not ideal and instead we want to do a bit of styling but it's going to be super straightforward we already have access to the session in here then if there is no session we're going to return a not found that we get from next slash navigation so we are certain that there is a session if we render out this page now we want access to the friends for the most recent chats that's going to be a weight and then we're going to use our super handy helper function called get friends by user ID and pass in the session.user.id to get access to the Friends these friends however don't contain the last message yet and we want to show the last message for each friend that we're rendering out to do that let's say const friends with last oops last message is going to be equal to await promise.all you already know what this does we can do a couple of calls at the same time and we're gonna map over the friends in here for each friend an asynchronous function is going to get called where we get access to the friend right here in the parameter and a call block is going to get executed in here we want the last message and let's take a quick look at a quick look at the redis helper do we support the Z range yes we do great that's what we're going to make use of now so in here we're going to call um const let's just name this last message this goes into angled brackets because we are destructuring the first element from an array that's why this goes into array signs because we're going to get back an array of messages as for the database query let's call it's going to be equal to a weight fetch redis fetch redis and the Auto Imports don't see seem to be working so let's restart our development window I do that every time the Auto Imports don't work and that has worked wonders for me because afterwards they always do let's let this load and then the fetch Riders is there as I said it's like magic and we're gonna fetch from the Channel or the redis you know store called chat then the chat href Constructor and then here goes the session dot user oops dot user.i dot ID and the friend dot ID now the um the order obviously doesn't matter because they get sorted in there and from those we want the colon messages from that assorted list and then oh obviously we want to pass it the command first because we're using the fetch writers the command is going to be a z range so we're going into a sorted list and getting a range of messages in our case we only want the last one that's why we're having this little destructuring here and then as for the arguments we're going to pass a minus one for the start index and also a minus one for the end index I'm just going to log this out for debugging purposes console log last message and also put that in string last message just in case anything goes wrong but this should be a message array where we can destructure the last message and that should be of type message now that we casted it into the type of message array and then we are going to return from each friend every property that the friend inherently has and the last message that we exchanged with this person great let's just quickly log this out to make sure we're not getting back a Json string from redis let's put the friends with last message in here and navigate to the page just to force the logging of this let's look into the console and we get friends did I call it friends it should be friends with last message did that get locked anywhere friends with oh because we're in the build right let's stop the build and go back into our development server so we can get the real time updates on the page while it is slower it does make more sense for development let's restart the page and now that should be locked to the console let's make sure it's not a Json string because otherwise you know this would work now it's loaded so let's go back into the console and we can see friends with last message is an array and we get the actual properties in here not as a Json string fantastic we can get rid of this console log and the other one if you didn't type them out you don't need to get rid of them and let's finish up the jsx um we're gonna have one div as the root level the top level element with a class name of container and a padding y of 12. inside of this div it's going to live in H1 with the text recent chats inside of it and this H1 is going to get a class name of font Dash bold a text-5xl and a margin bottom of 8. beneath this we're going to render out something conditionally depending on how many last messages there are if there are none then we don't need to show anything we can just let the user know there are none here to do that logic check we can say friends with last message dot length and if that is equal to zero then we're going to render out the following P tag and else we are gonna map over them so we can say friends with last message dot map and then for each friend that we receive as the first parameter we can immediately return some jsx and we indicate that by these parentheses and not by cardi braces if we return parentheses we can straight away start writing or jsx code in here first however let's do the P tag up here first and here there goes nothing to show here dot dot and this P tag is going to get a class name of text Dash small and text Dash zinc Dash 500. okay as for the div this is going to get a key because we're mapping over this the top element always needs the key and that's going to be the friend dot idea as the key value for this div with a class name with a class name of relative get rid of that down here calcium of relative BG zinc of 50 a border a border Dash zinc Dash 200 a padding of three and rounded of medium inside of the stuff we're gonna have another div with a class name of absolute that's why we marked this one as a relative so it positions itself relative to that diff above it a right Dash 4 and inset of y 0 so that's both the top and the bottom values in one a flex and an item stash Center inside of here we're going to render out a Chevron right icon that we get from Lucid react there we go Chevron right this is going to be self closing with a class name of height 7 with 7 and text Dash zinc Dash 400. then after the closing diff of that icon let's render out a link from next.js that links to the chat so for each friend we're rendering out a chat the Chevron right is going to be on the far right side to indicate you can go to that chat and then what should happen when you click on that chat we're defining right now with this link okay this link is not going to be self-closing instead it's going to get an href of a template string so this is going to be in curly braces a dynamic value to slash dashboard slash chat and then slash again or trusty helper Constructor chat href Constructor and then here we're going to pass the session Dot user.id and then secondly we want to pass the friend dot ID great the link is going to get a short class name that's going to be relative and small Flex okay inside of this link let's put a div first that div is going to get a class name of mb-4 margin bottom of four Flex shrink zero then in on small devices and up we want a margin bottom of null or zero and on small devices now we want to imagine right of four and then one last dividend here by the way I want to show you a cool little trick instead of typing out div and then the class name we can just do div Dot relative dot h-6 dot with dash six and then hit enter and that's going to put it automatically into the class name that's pretty cool and the reason we're marking this as relative is because we want to render out an image in here that we get from next image this is going to be self-closing as always with a referral policy of no referrer did I write that correctly yes I did because we're going to render out a Google image in there then a class name of rounded Dash 4. a alt tag that is going to be a dynamic value in curly braces because we're going to put a template string in there with the friend dot name and then as plain text profile picture you can already format that as the source we're going to use the friend dot image and then lastly this image is going to get the fill property applied so it fills out the next relative div in this case this div right here above the image okay after the image the image closing tag one closing div two closing div and then open up another div there's going to be right below the current closing tag of the link and inside of the stiff let's put in H4 with a class name of text large and font semi bold and here goes the friend dot oops dot name that we want to display to the user on their dashboard and then in here goes AP tag with a class name of margin top of one and Max width of medium so it can't exceed that size and a little Span in here with a class name of only one property that is going to be text zinc 400. inside of this span we're going to display the last message content that is going to be a logic check so either if I or you if the if the logged in user sent a message we want to display a little U tag and else there shouldn't be anything if the the chat partner wrote the last message to do that to do that check we can write friend dot last message dot sender ID and if that is triple equal to the session.user.id in that case we want to render out you colon and then a space bar and else just an empty string like that and also did I mess something up why is this red um because this question mark needs to be a colon there we go and then after the closing span we're going to insert the friend dot lastmessage dot text and that's it great let's save that I'm going to show this for one more second if you need to if if you missed something and then let's navigate to here and we can see that the message doesn't show up why is that the case we can see the and the loading set is something else that doesn't happen in production actually and we can see the last message however it doesn't seem to show up on the dashboard why is that not happening there's probably a problem with this one right here um so let's log out the last message and make sure this is not a Json string because I'm suspecting it is let's reload the page I thought we did this check earlier but I probably have missed it and okay so this is in fact a Json string this is why it's not working so this is not a message array but rather if we take a look at this and the last message is a string as string array so we can destructure the last message as one string and then let's call this or let's call this one up here last message raw and then let's call this one last message and this is going to be equal to Json Dot Powers the last message Raw as message so we can cast that into the corresponding type and that should fix the problem so what we did is declare this as a string instead of a message and then parse that Json and now it works so we can see U ASD if we click on that we get to the chat and the hydration error can go screw itself we can write any message hit post and then if we never get back to the dashboard later we will be able to see that message and why is this not posting hello are we getting any error no I think that the the environment here is just super slow let's reload the page type in any oh it's because the hydration error occurred right so luckily this doesn't happen in production it's super weird if I find a fix for it um I'm gonna put it in the readme file in the GitHub repository not too sure why this occurs but if we post the message now navigate back to the dashboard main one it should finish loading soon then we'll be able to see that the last message we sent right here was sent by us and this takes super long there we go okay we can see the last message we can click on it get to the chat that works super well okay great was that it is there anything else we need to do let me think okay so last thing we want to do is the um the mobile layout right because right now this doesn't fold it looks pretty weird if we try to do this on mobile devices and the demo I showed you in the beginning was well mobile optimized to achieve that let's go to Tailwind or actually this is called headless UI and then this is going to be a slide over let's go to actually no this is from Tailwind UI let's go to Tailwind UI in the browser this is a free component that we get access to them and then let's search for slide oops slide overs and hit that Tailwind UI is an assortment of nicely pre-built components that we can use some of them are free and we're going to make use of that so instead of typing all this out it's quite um you know it's not complex but it would take a lot of time let's put this into or application as copy paste this is going to do so much of the logic for the slide over for us that we don't need to worry about and so on the Tailwind website let's go to application UI overlays slide overs I'm going to link the page in the description as well and then we can just copy and paste the whole transition thing right here we're going to need to install one dependency for this um let's go back into our application and for the mobile menu let's create a new component navigate into our components folder and let's call this mobile chat layout dot TSX initialize this as a component and this is going to be a client component because whenever we're clicking a button to open and close the slide over that needs to be handled via an event listener and we can't have that on server components okay inside of here let's just uh let's just paste the code for now that we got from Tailwind UI that's totally good for now and now let's install the necessary dependency we need for this which is going to be called npmi at headless UI slash react hit enter if you don't know where I got that from I've worked with it quite a bit but it also says it right here at the top at headless to I slash react it's going to install the dependency at start the dev server back up again then let's start importing everything that we need to display this component the transition the fragment from react now this is what we're going to declare a state in a second we also need the dialog from headless UI so next to the transition let's import the dialog just like that is there anything else missing the xmark icon we're going to replace with the normal X from Lucid react we've already got that done and then the only thing missing is the state that keeps track of whether this slide off is open or not let's call this open set open it's going to be of type Boolean and false by default we also need to import the set from react and now at least the arrows are gone but currently there is no content in here and we don't have any method to open and close this menu let's do that first before implementing any content so we can actually see what this does at the top level we want to create a new div so even before the transition route that we're gonna wrap or whole component in with this div this containing div the top level div is going to get a class name of fixed a background zinc of 50 a border Dash b a border zinc of 200 at top of zero so this is always at the very top of the page and inset X of zero so left and right values of zero you can see that if we hover over it a padding y of Two and a padding X of four great and then one more div that we are wrapping the entire component with never mind we're not wrapping the entire component with that and this is going to be a diff that just lives up here um and we're not going to wrap anything with that okay this diff is going to get a class name of with full Flex justify Dash between to separate them as much as possible and items Dash Center for vertical alignment center there we go inside of here goes a link and this link is going to contain or logo right so we are building kind of an alternative nav bar that is being shown to the mobile users you're going to see that here in a second the logo should always go back to the home page just a slash dashboard slash dashboard and then this link is going to get a class name of button variants this is where the button variants are going to kind of come in handy because this should look like just a regular button now it seems like the button variants were not exporting from our button component so let's navigate into our button component and for the button variance let's say export corn spot invariants save that and now we can get them imported in here making the link look just like a button you can see if we had control on spacebar we can now pass a variant and that's going to be of type ghost so this link looks just like a button and then here we're going to put our icons Dash or dot logo with a class name of height 6 with Dash Auto and a text Indigo of 600 and this is going to be self-closing great below that let's put a button a custom button that we need to import from UI slash button this button is going to say menu and then next to the menu we're going to render out an icon that you know makes it very clear to the users okay this is in fact a menu that we get from Lucid react with a class name of height 6 and width 6. this icon is going to be self-closing great very very good the last thing we want is a on click Handler for this button where when we click this button it should set the open to true so this is the button that opens the sidebar for us with a class name oops class name why is this not working because I haven't left the on click with a class name of gap-4 just to separate these two by a bit flex is already applied by the normal button we don't need to put it here and now to make it show let's put this into our layout this is going to be the dashboard layout that we're going to put it into right let me verify that yes it is we're going to go to the very top of our dashboard layout and then right below the first div before anything else let's create another div with a class name of medium hidden so this won't be shown on medium devices and Bob and in here those are mobile chat layouts a self-closing component that we can import and let's see what happened let's navigate back into our app and force this to go into a mobile State and did I not start the server back up yes I did it just takes some time to load let's let this load and then we should be able to see the mobile layout but we also yes we see the mobile layout great with this slide over but we also need to hide the rest let's quickly do that and to hide the rest we can give this div right here below the closing div after the mobile chat layout this one right here let's give this a medium flex so it will only be Flex on medium and higher and a hidden otherwise it's going to hide the sidebar on mobile view if we reload the page those changes are going to be reflected and now we can see the chat looks way better and we get a slide over however that's from the right I would prefer to be this from the left to change that let's go back into our mobile layout and then there are two properties I think we need to change um so the first one we can see a right zero here um in the third div before the transition child it says write zero you can also type um press Ctrl and F and search for right to get to this value if you can't find it change this to a left of zero and then as for the transforms the enter from and the leaf from we want to select and just put a minus in front of those and I think that should be all we need to do let's verify that and okay that almost works we want to give it a bit of padding here on the side so it where it says padding left of 10 we want to change that to a padding right of 10. hit save and then let's try this again now it works but it seems like the X wait let's reload that and when it's done loading let's try this out the x is gone that's not ideal and you know what honestly there are so many CSS classes involved it's not hard to do but it's annoying to do and I don't see any value for you um it really doesn't add anything to the project essentially what we're doing is taking the base template from Tailwind UI and then what I did is go into the layout just copy and paste the code change a bit of CSS classes and make it look good for the mobile version I honestly don't think it makes sense to do this together right now this is just super annoying to dig into these CSS classes um so instead let's just navigate to the GitHub repository and copy paste the mobile menu I honestly don't see any value for you doing this so let's go into the repository under app not under components then the mobile chat layout let's just copy this component and you'll see there are a few missing values now those we will Implement together so we actually get a good understanding of where they're coming from let's just skip the whole CSS part it's really not the point of the project okay so for example there's a sign out button we can import that we've already created that together and we can see there's an image that doesn't exist yet let's import that from next slash image the session is apparently a thing we need access to and a friend request sidebar option I think I called it cyber options in this project um let's go friend request sidebar options why can't I import this yes sometimes the Auto Imports are super annoying let's reload the dev window and then hopefully that will fix the Auto Imports otherwise we'll just import it manually that's fine too let's see does it work no no it doesn't work so let's import it manually at the very top of the page let's say import friend request site and now it works what the hell now we can now we can import it I think I typed it wrong I think there's a spelling mistake here friend request what oh friend requests okay friend request Cipher options that's that's why it didn't work and we still need access to the Unseen request count and the session ID just like we do with the layout because essentially it is just a clone of the layout so there's nothing new in here just copy and pasting a bunch of CSS values and optimizing it just a bit for the mobile view okay we need access to the sidebar options the sidebar chat list we can just import that from our existing components and then the only thing we still need to do is pass in a couple of props to this component the first prop we're going to pass is going to be the friends let's accept them here in the component and the friends are going to be of type user array that's going to be friends there we go secondly we want to accept if the session and the type of the session is going to be session coming from next off we already fetched the session on the server we know it's valid so we can pass it to the child alternatively we could fetch it on the client again right here however I don't think that makes a huge amount of sense as for the sidebar options sidebar options that we're gonna pass into here as props we can just go into our main layout where we want to pass them from because we have defined them here and here we can see the type it's of type sidebar option now because we have to find the type let's cut that type out and go into our types dot d dot t s no that's not oars and so where does this type belong not really into either of these so let's um just put it somewhere else let's go into our types and create a type pings dot d dot TS and just put it in here because it really doesn't belong to anything else so now we get the type accepted here in the sidebar because we put it into a DOT d.ts file we don't need to import it separately and then here we can just say oops side bar option array then lastly what we want to pass is the Unseen request count just like we have in the regular layout because this is just kind of a clone optimized for mobile view it's going to be the same thing and we want to accept all those properties and the actual component now that should get rid of all the arrows there's still one left and that seems to be because we need to import the icons component that is not not that is imported so let's see what the error is element implicitly has an any type interesting so the icon that we defined right here is of type icon okay but that doesn't exist so we need to import it from our icon component now that should be either logo or user plus great and now there is an error here because we apparently need to import this type manually that's fine and then all the errors are gone great let's navigate into our layout.tsx and why is this giving us an error because we also need to import it here okay let's do that and then lastly we need to pass the props to the to the mobile chat layout inside of our layout.tsx we already have access to all of these values so this is just Child's Play the friends are the friends this session is the session the sidebar options are the sidebar options the Unseen request count is the Unseen request count and that's all we need to do now is this mobile chat layout user M reusable because we're passing it so many props no does it need to be no because we only use it in one place as I said earlier My Philosophy is copying pasting code is fine if you do it once or twice if you do it multiple times like three times four times then you should think about making it reusable which is always considered more work than just copy and pasting obviously if you just need a component in one place copy and pasting in my opinion is even the better way to go because it saves you a lot of time and there's no point in making it reusable okay let's check this out let's read out the page and see if the mobile menu is working uh there was an error there what was that about okay the arrow doesn't occur again let's try if we can force the arrow again text content does not match server rendered HTML I think that's one of those just like the the hydration error that'll just go away in production I don't really understand it to be honest it's only there during the page load and we might be able to work around it with an error handling an error boundary it doesn't really matter it won't be there in production so this is fine we can see the whole mobile menu now we can log out and as I said this is just a clone of the original layout and that's why I was fine with copy and pasting it it didn't really at least in my opinion added no value to you and then we can see the add friends we can go ahead in here add a friend but if we click an option the menu doesn't close and that's kind of a problem we still want to fix that and we can do that very easily by listening to path name changes in the mobile layout so whenever the path name differs from the current path the layout should close and the way we do that is we get access to the use path name Hook from next.js so const path name is equal to use path name that we get from next slash navigation invoke that and now we know the relative path of what we're currently on and then inside of a very tiny and cute use effect that we are going to put down here below the path name we're going to set open to false and run this set open every time the path name changes so what happens now is when I open the mobile menu and go to another page the menu automatically closes if I navigate to the chat you'll see it's gone it doesn't close smoothly because the chat is a anchor tag so it forces a hard reload but if we navigate to a page via the link tag it smoothly closes closes very very nice and I think we're done I think that's the project let's build it out then the arrows will all be gone yarn build or first let's run yarn lint so we know we haven't done anything wrong and we know this will build for production no arrows let's run yarn build and then we can go through the entire process one last time and deploy it actually I think it would make sense to deploy it first and then go through the process so let's do it that way actually I'm going to create a new GitHub repository for this um let's go to github.com I'm going to share my screen here let's navigate to your repositories and then I might need to censor some of these um uh actually no I don't need to Center any of these let's create a new repository let's call it something you you just call it whatever you want I'm just gonna call it friend Zone YouTube Let Me zoom in a bit I'm gonna mark it as private and create Repository that's going to create the repo the important thing if you're if you've never worked with Git is copying this remote origin then we can go into our project right here it's built successfully so we know it will deploy successfully and now we can say git add dot which means add all the files that we changed into our GitHub repo then we can run git commit minus M and then put a GitHub comment for example initial comets hit enter that's going to commit or changes and then we want to add the remote origin that we copied from the GitHub repository press enter and now we can say git push minus U for Upstream origin main press enter and that's going to put all the files into the GitHub repository if I reload the page you can see all the project files are in here now and you can see the first files are from three days ago the last one's from right now now to actually get this into the web it's really straightforward let me move this to my other screen so I will have an easier time in actually no I need to Center I need to censor this anyways and so I'm going to put it here yeah just to protect the privacy of some of our freelance clients I'm gonna have to censor some of this probably let's go to add new here in verse cell if you don't have a versa yet you can ever sell account yet you can log in with your GitHub and then go to overview and then we can can click add new and that is going to be a new project we will create I'm going to import the repository we have just created the friend zone YouTube right here press import and before we can actually deploy it we need to um put our environment variables in here so the project knows about the important stuff it needs to know about for example our next auth secret and for each key value pair we need to put it into versa so for example the next are secret let me put it in there copy the value we have in our DOT nvid.local put it in here and click add and that goes for every environment variable that we have it's kind of tedious but it will only take one or two minutes but every environment variable in here upstairs token this is the last one I'm gonna do with you and then I'm just going to do all of them and be right back when I am finished you do the same and then we'll deploy it together okay that's the last value I've added them all and now we can finally click on the big deploy button to get this into the web this will take a hot minute because it's gonna do the exact same we have done on the client already or in our vs code right here it's going to run the build command just the same and because we've already done it we know it will build correctly because there was no error on our local machine so we're just going to wait for this to build we can actually see the exact same build logs as we had in our personal console this will take a moment and then I'm gonna be right back when this has either finished building or has given us an arrow which I don't think it will and there we go it has finished building congratulations you just deployed a new project to versl let's try out if it actually works let's navigate to this go to The Domain first cell has given us and then for one last time let's clear our database I'm going to move this over to my other browser like that paste the URL in here and we can see the login page you can exit out of everything else and let's clear the database one last time so we get a fresh start let's run flush all on the upstairs CLI gonna flush all of our data so we are brand new to this let's click on Google and okay so what we need to do is after retrieving the Google client ID and secret what we also need to do is add this particular domain in our Google Cloud console to the authorized sources let's go to our Google Cloud console and quickly do that and I am hosting this all under my similarity API I didn't bother to create a separate project just for this and so we're gonna go to log in data that under or oauth 2.0 clients where you have gotten the client ID and client secret we also need to add this domain as an authorized JavaScript source and also for the authorized redirection Uris we're going to add our domain slash API slash auth slash and I'm going to zoom in so you can see this better slash callback slash Google because the Google callback will handle the verification then we'll click on save so quickly to summarize what we have just done add this domain right here and under the Uris and then secondly down here under the auth and the redirection Uris save that so um after the part you just saw in the video I tried out the application notice there were two more bugs that we needed to fix but I was really tired and just call it a day that's where we are now I know exactly where the bugs are let's fix them turn a few last dials and knobs improving the performance of the app and then we're done then it's all complete um however first off let's fix two more bugs that we still had in the code I'm gonna make this a bit larger for you and I didn't count this as a bug but the first thing we want to do just as a better practice than currently is make sure that the user we fetch as the chat partner is always and not stale right and to do that we are going to replace our db.get with our trusted fetch through Addis function just so we don't get any issues um in the future with that I'm going to disable GitHub copilot and let's first call the chat part oops chat partner raw let's call this chat partner Raw it's going to be equal to a weight Fetch redis and we are going to mock or we're gonna do the same request as we do up here just with our fetch redis function so it's also going to be a get request to the same exact thing we are fetching the same piece of information and this is going to return as a Json string so we need to type it out as that and then the const chat partner the same as up here we're gonna replace this is going to be Json dot parse chat partner raw and then we can get rid of the first line right here and we might want to type this out as user as well the chat partner and by doing that we have successfully replaced the db.get the last one we had in our application with a Fetch redis and now let's get on fixing the last two bugs that are in the application um as I said earlier to make it the best it can possibly be and the first thing we're gonna do is imp like really improve the acceptance API route as you can see currently this is not optimal Behavior we want to do two things first off adding a friend should also refresh the little sidebar right here the friend requests and by the way I just tried this out that's where these texts are from and it should also refresh the little one or two that is be besides the friend request right here currently it doesn't do that and that's kind of weird because you have to refresh the page in order for that to happen we don't want that that should be gone whenever you accept a friend and to do that we're going to slightly refactor the way this acceptance API rod works so navigate into the source app API friends except where we accept a friend and let's give this a little refactoring we're going to start right here below the has friend request and the first thing we want to do is fetch the user information and the friend information of the person that we are adding we can do this and let me zoom in even more so it's easier for you to see by saying user raw and then the friend raw as always these are strings but they are users just as a Json string so we still need to parse them that's why I call them raw it's going to be equal to in parentheses oh wait promise dot all and that's how we're going to improve the performance of this API Rod we're gonna do some calls simultaneously that don't rely on each other and then here inside of the promise.all we can actually pass an array of promises that we want executed simultaneously first one is going to be fed shredders for either the user or the friend information the order does not matter because they will be executed simultaneously they will both be get requests we can all already copy them down separate them with a comma and then as for the data in the first one it's going to be user colon and then the session dot user dot ID thereby we are getting the information like email and image and so on for this user and then secondly we want the same information for the ID to add as well and we can do Jesus we can do those calls simultaneously because they don't rely on each other and then we can because remember these are strings so we can also type this out as string string saying use a raw as a string and friend draws also a string and to get the actual data we can say const user is going to be equal to Json Dot parts of the user raw and then we can just copy this down and say friend cons friend is going to be equal to json.par's friend raw and both of these are gonna be as user by the way if you wonder how I typed in multiple places at the same time you can click somewhere and then hold alt and then click in another place and set your cursor in multiple and places that way and then we don't only want to notify the added friend but also the person that is adding to refresh their application State making the end result way cleaner so we don't need to refresh the page anymore to reflect the changes on the person that has accepted the friend request might seem a bit abstract for now but you're gonna see exactly what I'm talking about um so instead of all these requests we're gonna do them them simultaneously they don't rely on each other and that will significantly improve the performance of or API route to do that we're going to apply the same principle calling away to promise dot all and then here we can pass an array again of the promises that we want execute it first one we can just copy the user ID to add friends push or trigger where we trigger the new friend action and paste it up here in the promise.org but as a little modified version of this instead of an empty object you want to pass the actual user to the person that we are adding we can copy and paste this down separating it by a comma and then just change the ID to add to session.user.id and similarly the user we want to change to friend so our friend gets all data and we get the friends data that's what we're doing here and all these database calls all um database add database remove where we add and remove from a set they can happen simultaneously as well they don't rely on each other therefore we should definitely do them simultaneously we can just copy them in here separate them by commas and then get rid of all this I don't want to call it you know crap because it is good logic but it is not very optimized and this works in a way faster fashion because performance is very important in this application I mean we're using red is after all right it's it's important and that's a big Improvement we have just made to our API world two more things we want to do and they're pretty straightforward is first the when we add a friend in the browser the person that gets added their browser is not refreshed um reliably so when I add you as a friend then you won't always get notified and my name won't always show up right here on your client and that's not ideal to fix that bug let's go back into our project and then into the sidebar chat list right here in the components and what we are going to do in here is we are going to improve this logic right here this does not work not work reliably the router refresh so instead what we're gonna do is a better practice as well we Are Gonna Keep the friends in state instead so just like with the messages or the the stuff and the other components where we imported the initial stuff as props to the component and then put them instead to expand on them later on that's what we're gonna do now as well and let's call this for example active chats set active chat and that should be chats plural this is going to be of type message no this is going to be of type user array and then the initial value is going to be the friends so all chats that we have up here are going to be kept as state now that's what we're currently doing and then we want to pass the new friend to the new friend Handler as you said before we passed an empty object but because we have just fetched the data for this friend on the back end I'm going to switch back to the route just for a second and we've done that right here we're passing the user and the friend which is user data we get from the database up here in these simultaneous calls we are passing that to this new friend Handler meaning we can now easily put it in state and get rid of the not reliably working router refresh and instead say set active chats and receive what they were previously then create an array spread in whatever it was previously and also push in the new friend at the last index great and that's all we need to do in this component let me quickly verify that yeah that's all we need to do so we just changed the new fan Handler put them in state instead and now the active chats are going to be used to map over for the chats that we have visible in our sidebar very very good and then last thing we want to do to get rid of the error where when we add a friend the friend request a little symbol that shows up right here there's a little one it doesn't go away until we refresh the page and that's not good that's the last bug we want to fix the last bug in the application and we are going to fix that in the friend request sidebar options in the components directory in here we are going to change the logic and also subscribe to one more event and that event is going to be the friends so instead of just listening to the incoming friend request and subscribing to that we also want to subscribe by saying pusherclient dot subscribe to the to Pusher key and then in here goes user colon session ID colon friends that's the Channel we want to subscribe to and then we want to bind that to a function whenever the new friend action happens so what to listen um to this function whenever the new friend event happens that we emit right here on the back end in the add or accept API route we want to listen to this event right here the new friend to handle that change accordingly we're going to say pusherclient dot bind and did I switch back to English keyboard yes I did dot bind the new underscore friend and bind that to the friend request Handler the friend request oh no let's call this differently let's call this edit friend Handler there we go that function doesn't exist yet let's create it up here it's going to be super straightforward the const added friend Handler is going to be an arrow function and in here what we're going to do is we are going to subtract from The Unseen request count right we're incrementing that up here and now we are going to take one away so set unseen request count get whatever it was previously and just say pre -1 instead of pre plus one thereby whenever you add me as a friend and then the little icon that stands here for unseen friend requests will go away in real time without having to refresh the um your browser and then pay attention to the return statement we want to clean up after ourselves because we subscribed and bound a new function we also want to unsubscribe and unbind that so we can just copy the Subscribe statement up here where we subscribe to the new event of friends paste it down here and the return statement and say unsubscribe instead and similarly for the pusherclient.bind new friend we are going to unbind that um right here in the return statement and I think that was all the bug fixing we need to do I think we're done maybe I missed something I'm pretty sure I didn't but um let's just verify this let's build this application and test it out um for a final time I'm really excited for this I think it will work well now I hope I didn't forget anything oh and while I was just checking the files for one last time and go into the router TS and insert a colon here very important otherwise this would just return null the fetch writers right here so what we're missing is a call in between the user and the ID to add at least I added a little typo there that's why it didn't work okay great let's clear this yarn build this for the final demonstration of the app and then while this builds we can already go ahead and clear the database by the way let's navigate on here and say flush all for the final demo that's going to clear the entire database of any data everything is going to be gone and then we can start up the app great it's going to start the production version of our application we can log out and I'm going to open up a second client so we can verify that everything works correctly here on the right hand side let me log in with my support account here on the other screen there we go let's let that load and move this into a nope into a side-by-side view with the other client great okay so it's logged me in for that account let's log in with the wordful AI admin right here and then first thing we want to do is verify that the adding of a friend works let's type in support wordful.ai and if I think I zoom in if I zoom in anymore it's going to show the mobile view it's easier for you to see a bit more zoomed out but then you can see everything let me hit that that's going to show the friend request in real time right here and then when I press on the little check mark right here to accept a friend two things should happen that weren't happening before we fixed these bugs first the little one right here should disappear and because we're now keeping that in state and subtracting the original state and then secondly be over the overview um on top of it the wordful AI admin should show up as a chat partner and then similarly on the left hand client right here wordful AI support should show up as a chat partner for this person let's try it out let's hit the check mark and it worked just X just as expected that's why we fixed the Box very important the little one is Gone without having to refresh the screen the friend request is gone and both clients can now see the chat partner respectively if I just say hello hit post that's going to show a beautiful toast notification to the other user and because we're not in the chat if we were and send a message that posts on the that toast notification would not be shown and we can chat in real time super fast by the way because we optimized the hell out of our API Rod so to say um with the other user great we could spam the person if we wanted to that works just fine everything works really really well in this production build and last step is committing those final changes to the Repository really really good work very nice if you followed along congratulations you built a really nice real-time application and last thing we want to do is get at everything get commits minus M for a comment so now we're pushing those changes into our production build in the web saying something like fix Miner bug fixes and improvements for example just a commit message I just made up and then last thing we want to do is say git push and hit enter great and that pushed all of our changes into the actual production version now versel would rebuild all of this essentially versel will have the same version as we are hosting locally right here um because and I think yeah this is not showing up because we stopped the application let's start them you know while I'm while I'm talking through this let's just have it open and silver cell would have the exact same version as what we currently have because this is the production built out version the same diversal uses so the production one is going to be the exact same and everything works super smoothly great um again congratulations um if you followed along the whole video I know it was a very long one because in fact I've already cutted it and this was just the last step as a last summary um for the whole project I think it's like eight hours long it's pretty long and so thank you very much for watching I really hope you got a lot of value from this video I tried my absolute best to make it a very beginner friendly and two to make it very easy to take these good Concepts that you learned in this video and take them and apply them to other projects that you're going to build in the future that's one of the best ways to learn following along with me is a very good way to learn and then really helping to accelerate your learning is take the stuff apply to your own projects um something like you know Implement real-time functionality for a project that hadn't any real-time features before and authentication to your application if you're new to authentication and that sort of stuff right that's really how you learn thank you very much for watching um leave your feedback below and a like of the video If you enjoyed this was a lot of work um but I really enjoyed it so I'll see in the next one have a good one and bye bye
Info
Channel: Josh tried coding
Views: 107,760
Rating: undefined out of 5
Keywords: josh tried coding, nextjs 13, react, typescipt, redis, realtime, reactjs, redis cache, react.js, redis tutorial, redis database, react typescript, react js, nextjs react, nextjs react 18, redis crash course, react tutorial, nextjs, typescript, next js, react crash course, next.js 13, next.js, redis db, react js tutorial, redis cache tutorial, next js 13, next 13, next js typescript, nextjs typescript, joshtriedcoding, nextjs 13 tutorial, nextjs 13 crash course
Id: NlXfg5Pxxh8
Channel Id: undefined
Length: 508min 22sec (30502 seconds)
Published: Sat Apr 08 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.