Master NextJS 13: Build and Deploy a Modern Full-Stack App in Just 5 Hours!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
a lot of super popular websites on the internet use nexjs namely coin market cap Spotify and even open ai's chat GPT in this video You're Gonna Learn how to create your own API service using modern react and nexjs by the end of the video you'll have built and deployed a complete API service web application if you're new here my name is Josh I'm a web developer based in Germany and I've worked a lot with nexjs in the past couple of months especially Nexus s13 for my own startup but also for open source projects that I've done here on the channel don't worry if you only know the basics of Nexus that it's fine or if you've only worked with Nexus 12 we're going to go from simple Concepts and then move on to Advanced topics later in the video the website is going to be fully responsive it features a beautiful light and dark mode and we're also going to implement rate limitation for the API servers that we're creating together the table that we're going to use to show users their past requests is super functional we can hide and filter by columns we can sort up and down it looks really good in light and dark mode and is super functional alongside building your API service you're also going to learn how to use typescript and Tailwind CSS both of which are really popular and in demand you'll learn how to handle loading States in xjs for the best possible user experience and also how to protect sensitive roads so nobody that is not authenticated can access those routes as I said we're going to start simple and then move on to some Advanced topics first we're going to lay the groundwork for the project and then move on and build step by step and I'm going to take you with me all the way from the first dependency installation up till the deployment we're going to deploy this project together it's going to be the full journey in this single video okay so before diving into the code together and coding this all together let me show you and demonstrate to you exactly what we're going to build together so this is the home page it looks pretty nice we've got a little 3D animation here for the 10 text and we can toggle between a beautiful light and dark mode and by default the system mode will be enabled we've got this little image right here that I'm also going to provide to you if you want to use this for this application we've got the documentation page which is fully mobile optimized so you can also scroll to mobile it has animated code for node.js and for python little tabs that we can switch to also we have a full authentication feature so we can sign in this will work with Google Now my Google account has already logged me in automatically that's why I didn't prompt me to log in again and in this dashboard we can generate our API key that we can use to make requests to the endpoint in the documentation and get the similarity of two texts so to try that out let's request an API key that's going to generate a unique API key for us that we can then use right here in Postman to make a request we can pass this API key in the authorization header and in the body we can pass along the properties we demand in the documentation so text 1 and text two we can click Send that will send along a request to this API route with or authentication token and because we generated this token for ourselves we know it's valid and the similar similarity between these two texts is 0.83 that's only part of this project though making that API service if we refresh this page it's going to prepare our dashboard where we can see a beautiful table with all API requests that we have ever made to this API endpoint with the duration it took to generate the answer the status the recency the path and the API key we have used there's a bunch of options right here we can copy the API key into our clipboard we can also create a new API key so the old one will be invalidated and we can check that we've just invalidated the old one we can send another request and right away the error on authorized pops up we are not allowed to make the request with the same API key we could also just revoke the key so it's not valid anymore and right now we don't have any valid API key and we would have to request a new one for this to work so this is really cool I'm very excited there's a lot of cool features in here and I think we can now get started in building this out together step by step starting at the simple Concepts and then step by step moving towards the advanced concepts okay before getting started with the actual project I just wanted to let you know there is a GitHub repository for this so if you get stuck at any point you can easily compare the code Euro to whatever code is right here in the repository and get unstuck that way okay and with that out of the way let me close this down and let's get started with this project from scratch and to do that I'm gonna press Windows button in R type in CMD to open up the command line and then we can get started with a whole new next year s13 project on the desktop so I'm going to navigate to the desktop and then say npx create Dash next let me zoom in a bit Dash app at latest so that means we're going to be using the latest version of Nexus which is going to be 13.2.3 I believe and then dash dash experimental Dash app and then hit enter it's going to initialize the nexjs app for us we're gonna go we're going to call this similarity uh Dash YouTube because I've already got this similarity Dash API where I coded this out before filming this video we are going to use typescript however as I said I assume you're completely new and I'm gonna walk you through every step we also want eslint so we can get um you know better code practices implemented yes we want the source directory and then we're going to configure the import aliases ourselves if you don't know what that means don't worry um it's um it's something that we can do inside of our projects to make the Imports look a bit cleaner so instead of having the dot dot slash dot dot slash all the time in the Imports we can just use a an absolute path from the root of our project and you're going to see what that looks like here in a second so let's open up this project open with vs code it's going to open here on my left and here we are inside of a brand new project within Nexus 13. okay now the first step would be to install all the necessary dependencies that we need so we can open up the terminal here on the bottom and close this for now and we want to install a bunch of dependencies that we're going to need now to determine which ones exactly we need these are the ones we already have and we're going to extend them right now so I'm gonna say yarn add but you could also say npm install it really doesn't matter which package manager you use and then I've got the package.json here on my left side and we are going to start installing some dependencies so first one is going to be at emotion react and the second one is going to be at emotion slash styled and then um the add mui so we're going to be using material UI for this slash material and also add mui slash system and add mui slash X data grid now this data grid right here is super interesting because we're going to use this data grid to handle the logic inside of the API dashboard so the the table I showed you in the beginning that is done with muix data grid super powerful library that we're gonna use and I'm going to show you how to use then for the authentication we want the next dash of Slash Prisma adapter Dash adapter and that we're going to use to handle the sessions in or database so we're going to connect the next auth with the database that way then we want at Prisma slash client for the client-side types of Prisma because we're going to use Prisma for database management then we want um at reddex Dash UI slash react drop down Dash menu and also adds red x dash UI react scroll area super handy and also add Radix Dash UI slash react Dash tabs and that's all we need from Red X UI if you don't know what Radix UI is it's an unstyled and in my opinion the most underrated Library there is where the xui it got acquired recently so they stopped marketing their um their um you know components but they're Primitives so you can style them however you want but the accessibility and the features are all in there it's super underrated in my opinion and we're gonna leverage the full power of Red X UI in this project that's everything with red X UI that we want at total Dash type script reset to get some better types within our project we want the rate limiting stuff that's going to be really cool um at up stash slash rate limit and oops rate limit and at upstairs slash redis and we're going to use those two for the red limiting then we want class Dash variance Dash authority to handle reusable classes for or components then we also want clsx and date Dash FNS to handle some date utility functions we want actually yes lint is probably yeah it's already in here we don't need to install that eslin config next we've also already got in here perfect we want framer Dash motion as a peer dependency because I think that is for Red X UI they depend on framework motion then we want low dash for a certain scrolling Behavior within our application you're going to see that later we want loose seed Dash react for icons we want Nano idea for generating the API Keys we want next okay we already got that we want next Dash author authentication the the best package for authentication in Nexus in my opinion and also next Dash themes for the beautiful dark mode you saw in the beginning of the video open AI for creating the actual embeddings you're gonna see what that means later then a prism Dash react Dash renderer and yeah this is a lot of dependencies but this is a real project that we're building together and in real projects you are going to have a lot of dependencies that is completely normal um the prism react renderer is for the documentation page for the code highlighting that you saw earlier and so for this Behavior right here when we go to similarityapi.com the behavior you saw on the documentation page let me close this down right here the animated code that is what this highlighting is for the prism reactor underwear then we want Prisma for our database and then that's almost it we still won't react actually no we don't need that at all no then we want react hot toast to extend that for the toast notifications we want sharp for better image optimization with index.js simple bar Dash react to make this component I just showed you a mobile access boom so that means when I make the pageway smaller we can navigate this with this horizontal scroll bar and then there's two classes two dependencies left that we need which is one Tailwind Dash merge and the other one being Zod for server side schema validation and that's it I'm gonna hit enter this is probably going to take some time to install and we're just going to let it install and see what happens I think or I hope I typed in everything correctly and you did too um if you forgot some of these dependencies remember they there is a GitHub repository you can go to the GitHub and then enter the package.json and see all the dependencies that we need right here let me close the stone and let's wait for this to finish installing great so it has finished installing all the necessary production dependencies that we need one thing we still want to do is install Tailwind CSS so let's get to Tailwind CSS for next JS there's a specific guide for that and don't worry if you've never used Tailwind CSS I'm going to walk you through it every step of the way so to get started with Tailwind we want to copy this Command right here npm install or yarn add these three dependencies as Dev dependencies so let's open up the terminal and then say yarn add Dash D or npm install Dash D for Def dependencies and let's remove the duplicate and then tear one CSS CSS in auto prefixer and we also probably because the next 3s uses npm by default once you delete this package log specifically if you're using yarn if you're using npm then that is just fine and then run that again really quick perfect we got rid of the unnecessary dependency look and then we want to initialize Tailwind CSS now to do this we are gonna run yarn and then Tailwind CSS init Dash p to initialize turbine CSS that is going to create a tailwind.config for us we can close the terminal and to use Tailwind CSS within our project we can copy this Tailwind config that they've already laid out for us into right here and save that because we're using the source directory that we enabled in the command line interface at the start of the video we are going to use this line right here and that is just you know to determine which file paths detail when styling is going to be applied to and last thing we want to do to get Tailwind up and running is copy these three directives into our globals.css and it's going to be at Tailwind base components and utilities and let us see what happens so I'm going to type in yarn Dev or npm run Dev if you're in the npm environment we're going to allow this and then that is going to start up on localhost 3000 so let's do a little split screen right here and let me go into my browser type in localhost 3000 and give this a hot second to load and that seems to have worked just fine um yeah the split screen yeah there we go this looks better all right and then in here let's go into our main page.tsx and sea of Tailwind is set up correctly we can get rid of everything that is already in here then just render a main that says hello world and let's give this a classm of background red 500. this is if Tailwind is set up correctly and it is great so we see Hello World with a background weather 500 and that means that we set up Telvin correctly great I think the first thing that we're gonna do um is create reusable UI components and get some of the folder structure laid out so we can then work confidently within our project I'm gonna get rid of some of this stuff right here we don't need those we can delete the fav icon in the module.css and the rest is necessary so two components we're going to be reusing across this whole application are going to be a paragraph and a large heading so let's create them we're going to go into our source directory and then the app and then here we're going to create a new folder called components and inside of this components folder we could also create the UI already for components that we're going to reuse very often that are um kind of unstyled and then in here we're going to create the file called paragraph dot TSX to create our paragraph component now um I've got a really handy shortcut that is FC and if I hit spacebar that's going to create a typescript component for me like this so we can see the cons paragraph and then the FC which stands for functional component within typescript and this functional component takes a generic now a generic is some some abstract concept that typescript typescript gives us essentially I'm telling the component that the paragraph props we can Define up here are going to be the type of the props that we get in this component so if I were to say we're gonna get a you know text for this component for example which would make sense because this is a paragraph after all then typescript wouldn't know which type this text is and therefore we could put it up here and then say this text is of type string or you know Boolean whatever we want now Boolean wouldn't make sense for a text specifically but I think you get the concept so we're telling react that this is a functional component type and then passing the type of the props that we're going to receive and that we Define up here now this paragraph is not going to take the text like that but instead we want to make this really reusable so I'm going to say const paragraph variance so we're going to Define certain variants of this paragraph now this is going to be super interesting because this is going to make this component very reusable across the whole application and to Define these variants we're going to use something called CVA now CVA comes from a dependency we've installed already which is called class variance Authority so let's import CVA from class variance Authority that should work just fine and what this allows us to do is first thing we can pass into this function is the styles that will always be applied to this paragraph now in in this case of the application that's going to be a Max width of Pros which is a Tailwind term we can determine or we can use to give this a maximum width of a certain prose which is like I'm not sure how much exactly that is can take a look at that Max W Pros hover over that that is 65 CH that is the width we're giving it right here we don't want that on the div and then text slate of 700 that is going to be the text color equivalent to CSS then dark a text slate 700 300 700 was the light mode a margin bottom of 2 and also text Dash Center great let's format that using prettier and those classes will always be applied to this paragraph and then as a second argument or parameter for this function we can pass the actual variance we want to exist of this paragraph and that's going to be the size of the text for for one the default size of the text is going to be text Dash base which is a Tailwind term to determine a font size and then we also want a small of text large so on mobile devices the text is going to be a bit bigger so it's easier to read that's going to be the default starting of this paragraph and then we also want a small variant of this which is going to be text small and then also small text based on mobile devices this is all again a bit larger and better to read and then we can pass the default variance for this default variance so right now we've defined the certain properties that this paragraph could have but we've not told it what it should be by default so we call this default but it is not the default value yet we still need to set that in here so we're going to see the size by the way I opened this interface with control and spacebar so we can get the full type safety the size is going to be default or small both of which we've defined up here and we're going to choose default okay great now we've defined our paragraph variants that this paragraph will always have and now we want to Define some props this paragraph can take and because we're working in typescript we can leverage the full power of typescript to do this and extend this interface by the HTML attributes that we get from react it's a type this takes a generic again I've already explained what a generic is it's kind of like a function in JavaScript but instead of passing parameters we are passing or instead of passing arguments we're passing types and then here we want the HTML paragraph element so that means wherever we render this paragraph it let me go into a different page right here say we wanted to render the paragraph right here we can import that and then as you can see when I see which props we can pass we can see all the props that a paragraph element in HTML could normally get because we did this extension of the interface right here and then also we want to extend this by the variant props this takes a generic again and let me format this this takes a generic and we want to pass the type of paragraph variance right here so what we just did if I save that is instead of the normal props that a paragraph could always take in HTML now as you can see we see the size option and if we see what we can pass as the size it is either default or small so whatever we have defined in our paragraph variance up here and that is super useful because now we can render the small size and the paragraph will always look the same wherever we render it um in the application why is why is this not working again like that okay great now the styles are not being used in the paragraph just yet to do that we are gonna turn this into a forward ref at first so what we're doing right now we're importing the forward ref from react and what the forward ref does is it allows us to pass a ref so a link to a certain Dom node to this component and you're gonna see what that exactly means later on so if I go back and we try to pass a ref to this component we couldn't it doesn't take a ref property however if we use this forward ref then wrap the whole component within that and also import that from react then we could pass a ref to this component and as I just said a ref is a link to a Dom node and this ref takes two generics this time so again generics what we did up here already and the first one is going to be the type of whatever we're going to be rendering it's going to be an HTML paragraph element and then the second is going to be the type of the props that we're gonna pass which is this paragraph props we've already defined up here so we can pass that as the second generic and now we can see all the props that we could receive in this component now we are only going to destructure oops gonna destructure a few of those and those are going to be the class name property that we could pass from anywhere where we render this paragraph element then we want the size the children and everything else we're going to receive as DOTA dot props now the second argument because we did the forward graph is going to be the ref to an element that we don't have to pass is optional and but it is a best practice to always allow um accessing this element programmatically from wherever wherever we render it great now in the return we're going to have a p because this is a paragraph element after all the ref we're just gonna forward um to the ref that means if I were to render out the paragraph in this component right here and pass a ref of you know I initialize a use ref up here and then pass it in here then essentially that will create a link from that ref to this paragraph element right here that's why we're doing this but we don't want to pass the ref just yet then the second one is going to be the spread end prop so whatever we pass from the parent we don't even care we're just going to pass it on to this paragraph component the class name is gonna be a dynamic last name and for that we're going to define a utility function now utility functions let's create a folder and this folder we're going to call Lib for Library essentially meaning that we are preparing certain libraries inside of this folder to be used in our application and I can make this way larger we don't need the website right now okay now we've defined the lip and then here we can define a folder or for an orchest that's just going to be a file called utils.ts because it's just going to be a few utility functions it's nothing special actually it's just going to be one and this function is going to be called CN for class name this is going to receive a bunch of inputs of type class value array now if you if you never use typescript the way we Define a type inside of a function is inside of these parentheses we can say a colon and then the type of whatever this is not this type I didn't Define myself this comes from the library but it doesn't come from that that is a wrong automatic import it comes from clsx there that's where we get this um type class value from we can all say import type that's going to make this a bit easier and then this function is going to do some merging for us so we're going to return a TW merge that is what we get from the Talon merge Library we've installed at the beginning of this video essentially turning codes like you know if we were to pass padding y of 2 and padding X of 2 um to a component for a class name then Tailwind merge will turn this into padding of two so it optimizes the class names that we pass it and just for some better you know readability and maintainability and in here well first off we need to import that Tailwind merge so let's say import TW merge from Tailwind merch that's just right GitHub copilot and then in here we're going to pass the class name clsx and just input the we call it inputs there we go okay so this is going to handle the conditional Logic for us and Tailwind merge is going to merge the classes together for us that's what this is going to do and we can call that inside of the class name for our component right here so we can import the CN function we have just created let me go back to the paragraph here and then in here we want to pass the paragraph variance and just spread in the size and the class name that we receive to use the variance to also use whatever class and we pass it wherever we render this component and we're almost done with a paragraph last thing we're going to do is render out the children right here there we go so whatever is inside of this paragraph wherever we render it so say this wasn't a self-closing paragraph but there was some text in here the sum text would be considered the children to then be rendered out right here and that is what we're doing great I think we are almost done yeah we're almost done with our paragraph one line is missing paragraph dot display name this is for debugging purposes by the way the display name is gonna be paragraph so we do that because we're using a forward ref and we also want the display name inside of our component right there and that is our paragraph done great we can get rid of all the unused types and we're going to do something similar for a large heading so let's say large or we could just call this heading I'm going to call this large heading because it is going to be pretty large and what we can do is just copy this whole thing and change some stuff about it so instead of a paragraph This will be in H1 obviously we don't want the P tag we want an H1 tag we want to change this to an HTML heading element and the paragraph props are not going to be called paragraph props but large heading props now this doesn't exist yet because we need to redefine this then that is going to extend by the HT oops HTML heading element instead of the paragraph element and we don't want paragraph variants but heading variance now these don't exist yet we still have to define those so first let's define which classes this heading element will always have it's going to be a text of black in dark mode that's going to be white though then a text of send oops text Dash Center on large that's going to be text left just for some good responsive design then font is going to be extra bold so super bold leading Dash tight that means the letters are going to be closer together and then tracking Dash tighter actually that might mean the letters are closer together I think they essentially mean a very similar thing okay now for the heading we're going to Define three sizes one is going to be the default this text is going to be way larger than the paragraph because there's a heading after all so we want the text before Excel then on medium devices we want it to be 5xl and on large devices this should be 6X XL then the small variant is going to be a text of 2XL medium text of 3XL and large text of 4 XL and we also want to define a large variant for this large heading which is going to be text Dash 5xl so essentially incrementing all these by one we can just copy paste them and increment this to five this to six and the last one large text to 7xl also put a comma in the end and the default variance we can leave like that great and instead of the um the heading variance instead of the paragraph variants we want to pass the heading variance down here containing the size and the class name and the rest looks pretty good so I'm satisfied with this large heading component we can save that and by doing that we have two essential building blocks when it comes to the later stages of our application and building this whole thing out a bit easier so now we have everything we need to get started with building the home page so we can go to the main page dot TSX and inside of here we're going to build this out however before we do that I think it makes sense to get the layout right because we are going to be using certain fonts in this project and we want to use them throughout the whole application we can close all the tabs that we don't need we've already imported the globals.css grade some metadata we can get rid of the metadata because we're going to Define that on the page layer and also this import is not very pretty we want the absolute Imports right and to do that we can go into our tsconfig.json and that is going to make the whole project way prettier and we're going to Define some custom paths in here we do that right here and we want to Define five paths in total one is already defined then we want to define the add slash styles slash anything and just pass that to Source slash style slash anything that we want and essentially what we can do that by doing that if I save that and go back into the layout we can say add slash Styles slash globals.css and we can totally get rid of the sold dot dot and Dot and slash dot dot whatever import naming convention just break out of that and use this prettier format for defining Imports now we don't only want that for the Styles we also want that for the add slash lib slash anything that's gonna forward to Source lib then we want the components so add slash components slash anything is gonna pass the import to dot slash source oops dot slash Source slash components slash anything that we want and the last one is going to be at slash UI slash anything and that is gonna go to dot slash Source slash components slash UI slash whatever we want and save that in our tsconfig now we're getting an error right here that the globals can't be resolved because we haven't put that into a Styles folder so we're going to go into our app directory create a folder called Styles and just put the globals right in there and then the error should be gone in a second great now let's get this layout sorted out it's a crucial component for the whole application that's why this layout is super important and we want to Define only a couple of things in here first thing we want to Define is the font so we're going to say we're going to use enter for this project so cons enter is going to be equal to enter and then subsets is gonna be an array that takes Latin as an argument and the inter comes from next slash font slash Google we can import that so import enter is a named import from next slash Google and this enter we can use in or HTML element right here which is also going to use the very very useful CN utility function that we've already defined we can import that that is going to be from add slash lip slash utils that should be Source lib oh and the lib should be in the source directory not in the app directory and that goes for the Styles as well so let's move that into source and that also goes for the components so let's move them out too there we go so now these paths are actually working and let's restart the okay vs code seems to be loading right now whatever we can restart the typescript server in a second and for the HTML class name right here we're going to call the very convenient utility function and then here we're going to pass some default Styles so background is going to be a white that goes for a whole page then the text is going to be slate 900 by default the we're going to apply some anti-aliasing NT anti-aliers anti-aliers there we go and then to merge that with the font we say intro dot class name and apparently the typescript server can be restarted now great let's try that and let's see if the import still throws an error or if that works and we can close this by the way there we go it seems like vs code has some problems moving the components into the source directory so let's save this really quick exit the vs code and then move this manually so we can go into source and then components and then drag it out to similarity YouTube same goes for the lip and the style oh no that should no that should still be in the source um so component they should be in the source but they shouldn't be in the app directory there we go okay so at the source level we have the app the components and the Styles and the lib that's exactly what we want so we can go back open this back up in vs code sometimes vs code really isn't the best at doing that sort of stuff but now it works just fine and now let's finally get the layout sorted out the body is going to receive a class name as well and that is going to be a Min height of screen so it's going to take up a minimum height of 100 VH that's the CSS that you can see if we hover over this there's a background slate of 50 gonna be applied so a very very light blue almost white-ish you're gonna see that in dark mode by the way we declare dark mode differently if we just put a dark colon in front of this it's going to be BG oscillate of 900 and also anti-alias NT there we go anti-aliased format that using predator and great this looks really good now let's do the groundwork the foundation for the later project stuff so and here we're going to insert them some components that we don't have yet there's one gonna be the providers so we're going to be providing context to all the children in the whole document using these providers right here now this component doesn't exist yet so we're going to create it it's a very very simple component that doesn't take long to do so we're going to go into our components and then a new file called providers.tsx now I'm going to initialize this as a functional component but if you don't have the snippet then you could just write this out yourself you could even leave this whole typescript part out because this provider is only going to receive one prop that we can also Define inline if we wanted to by the way if you're wondering how to do that we can do this like this children is going to be of type react node like that we can Define this inline if we wanted to but I kind of prefer the functional component approach I think it's a bit easier to see but this works just fine as well but this react node essentially means we are receiving react components like the children that we have right here are going to be passed as children to these providers and because we're working in nexjs server and client component architecture we're gonna have to use the client director for this component because if we want to provide the context to different children in the application we have to use client components for that we cannot use those inside of server components that's why we're declaring a separate client-side component for this and we need two providers for this whole application first is going to be the theme provider that we get from next Dash themes so we're going to import the theme Provider from next Dash themes and this is going to allow us to do oops themes this is going to allow us to do the beautiful light and dark mode throughout the application it's super simple to use and I really enjoy next themes and then the second one oh by the way we still want to pass two properties to this one or three the attribute and I think my vs code is still loading again sometimes it's a bit slow but we can just type this out ourselves it's going to be class so we want a class-based strategy the default theme for this is going to be system so whatever the users are already using on their system whether it's light or dark mode by default and then we want to also enable system there we go and then the second provider is the session provider that we get from next auth and also close that off because vs code is slow as hell and it's still loading session provider there we go now it's finally loaded and then here we're going to render the children and that's all the providers we need for this application now the session provider we're going to use for client-side authentication in some components main authentication is obviously going to be server-side but we do want this to use client-side authentication in some places because we have to and we're going to import the session Provider from next Dash auth slash react that's where we get it from and that is our provider component done already now we can import it right here in the layout.tsx main root layout component that we have in our application great we're going to do one more thing at the end of the body and that is I'm going to comment this in as well and change the German keyboard for this nope that's not what I wanted to do like that we want to allow for more Heights on mobile devices because we're gonna have a very neat mobile menu we want to render a self-closing div in here and the only purpose this div has is to make some space at the end of the page so the mobile menu is a bit easier to access the height of 40. and on medium devices and up that is going to be hidden great and also instead of the providers we want three more things that is going to be the navbar a toaster and a mobile menu so let's just do that this might seem a bit abstract right now that we're doing all this without seeing any results here on the right side but it is really important for the later Foundation of or application so first one is going to be the nav bar now that navbar doesn't exist yet so let's create it and I promise you with creating the nav bar we're going to see the first results here on our actual page and it's going to be really cool so let's create a nav bar in our components let's create a new file called navbar.tsx this nav bar is going to do a lot of stuff for us if you take a look at the finished product that we're going to build in the end let me close that this is going to be the nav bar it has the main page it has the dark mode toggle that we're going to build a link to the documentation page and also a sign in button so let's initialize the navbar as an FC component and by the way if you're getting annoyed that I'm using this FC all the time you can do the same if you go to file preferences and then configure user Snippets and then let's go to code Snippets right here no that wasn't a one preferences configured user Snippets I think it's in here it's this snippet right here I'm going to link it in the description you can literally copy and paste it into your typescript react.json this FC snippet and then you're gonna have the same snippet as I do you can just type FC press Tab and then I have a full component in here and it's going to be really helpful and time saving okay let's build this navbar and now we're going to really leverage the architecture of an extra server-side components so we're going to declare this as an async component and that means and here we can get the session of the user so concession is going to be equal to await get server session that we get from next off now we haven't configured next off yet so this doesn't make a whole lot of sense yet but we're gonna render the navbar conditionally and the authentication is the next thing that we're going to do after building the home page so this is going to work buttery smooth here in a second not from this Nerf Bar we want to return a div as a main component of returning with some class names this is going to be fixed if we take a look at the CSS equivalent that is going to be position fixed then a backdrop Dash blur of small to give this a nice blur effect in the background a PG white slash 75 and in dark mode that's going to be a BG slay oops slate 900 I'm going to make this full screen because this class name is going to be a bit larger then we want to give this a z index of 50 a top zero so it's always going to stay at the top left zero right zero so it doesn't have any gaps on the side then we want to give this a height of 20 a border on the bottom a border of slate 300 and in dark mode that's going to be a border of slate uh slate 700 a shadow of small we want to give it the flex attribute so we're going to use display Flex for this which is going to allow us to have some control over how the the children of this River um positioned so we're going to say item Center and justify between the speed between so the items are nicely spread apart in the nav bar and then let me move this div to the new line right here nope I want it down here please there we go and that is the long name of the nav bar gone now the rest is going to be way easier we're going to initialize a second div right here the class name is going to be container now this container we're going to create it right right after writing out this cluster because right now that or actually that is built into Tailwind right I forgot yeah that is built in we don't even need to configure that this already works this is going to be a container it's going to make the mobile view way prettier and Max Dash width of 7xl which is I think 1 200 and uh why doesn't this why did I spell This One Max oh yeah this needs to be Max w-7xl that's why it didn't work um it's a Max width of 80 Ram we want to give this an m x dash Auto to automatically be positioned in the center then a w of force or for width Flex justify between and items Center okay there we go we've created the div inside of the div the first thing that we want is a link to the home page in the top left so we're going to create that it has an href it's a component that we get from nexjs it's super handy instead of having to use react router we're going to use file based routing in next year s13 leverage the full benefits of that and to do that we're gonna navigate using this link component that we get in next.js classroom of the link is gonna be of button variance button variance and I think we didn't ex oh no we don't have the button yet that's another UI component that is super important that we're going to create right after and the variant of this button variant is gonna be of type link there we go so essentially we are gonna create a link but with the styles of a button that's what these variants are for and then we're going to say a text similarity 1.0 inside of this link component now let's let's do this component fully and then worry about all the stuff that we don't have yet I think that makes more sense let's create a div right here with a class name of medium hidden and this is going to take a theme toggle that is the beautiful theme toggle there we go that is the beautiful toggle that we're going to create to adjust between light and dark mode then below that we want another div like that's with a class name of hidden medium flex so this is only going to be for medium devices and up and a gap of four so this theme toggle is going to be for the mobile view inside of this div we also want the theme toggle we want that on desktop too we want a link component that we get from nextges the same as above with an href of Slash oops slash documentation like that this is going to get a class name of button variants as well so we're going to import those here in a second button variants and that is again to give this link the appearance of a button even though it is not a button per se that is going to be variance of ghost so this is the ghost style button that we create up here and if we click it it actually looks like a button that's what that's what this right here is for and this link is going to say documentation because we're going to link the documentation page in there and now we are going to differentiate between the session and if we don't have a session so if we have a session by the way conditional rendering in react we're going to use some curly brackets or curly braces and then the friendship between the session states that we determined up here so if there is a session then we're going to render the following and if there is not a session we're going to render something else which is going to be a sign in button so if the user is not signed in we want to render a sign in button and if they are signed in we want to render something different and so we're going to have fragments in here so we don't have to wrap what's about to come which are going to be two children in a div because that's kind of unnecessary the fragment is just to do this without rendering another Dom node the link is going to be in here with a class name of um also button variants button variance call that with a variant of ghost variant cost to give this a ghost like button appearance this link and then the href that this demands is going to be the Slash slash dashboard there we go and inside of this link we're going to say dashboard this is going to link the user to the dashboard page and then we also want a sign out button now these two don't exist yet the theme toggle doesn't exist and the button variants don't exist yet either the question is which one do we get started with I think because the sign out and sign in button are very closely linked to the button component it makes sense to start with the button first we're going to create that the same as we did with the other components under the UI and then a button dot TSX now this button is going to take a lot of properties but it's going to be super useful across this whole application so let's get started in creating this one so I'm gonna use the FC again to create a component and let's define the variance first you already know the drill that's all the spots and variants and this is going to be a CVA again in first thing the CVA text remember is the default props that we want the button to have this is going to be active of scale 95 so when you click it it scales down a bit it's going to be inline Dash Flex items Dash Center justify Dash Center rounded off medium so a bit of Border radius just because it looks prettier you can get rid of that round at medium then text Dash small font of medium Trend oops Trend transition Dash color so we get some Tailwind color transitions in this than a focus property of outline Dash none focus is going to be ring two so we get some kind of outline when we click it also a focus of ring Dash slate Dash 400 focus of ring Dash offset-2 so there's going to be a bit of space between the button and the ring that happens when we click it and disabled we want the opacity to be 50. so users can clearly see that the button is disabled and which makes for better user experience and then on dark mode the focus is going to be a ring slate 400 in the disabled State we want the pointer oops pointer Dash events to be none so there's no interaction possible with this button in dark mode we want the and also Focus so if the if the user is in dark mode and just click the button then we want the ring offset to be slate 900 so just just the dark mode color of that ring offset and that is all the default um styles that our button gets and now we can actually get started in defining our variance so let's put a comma here at the end and then pass an object that contains the variance property now you already know what we're doing right here so first one is going to be the variant of the button and in our case we're going to have a default button which is going to be background slate of 900. text white it's going to be with a hover state of background slate 800 a focus no no now I got sucked into the GitHub copied suggestions that's not what we want so a text White when we hover over this it's going to be background slate 800 in dark mode the background is going to be slate 200 dark mode the text is going to be slate 900 and then when we're in dark and hover then the background last property is going to be slate 100 and that is our default button now we can take a look at that very soon at this button component we also want I think yeah three more so first one being the outline so whenever we want to have a nice complement of the default button we can use the outline button which is going to get a background of slate 900 as well text of white hover of PG Dash slate-900 in dark mode that background is going to be slate 200 also in dark mode the text is going to be slate 900. and then when we're in dark and hover then the background will change to slate 100 so BG Dash slate Dash 100 the border is gonna um like there is gonna be a border on here the border is gonna be slate 200 in light mode and if we hover over this outline button then the background is gonna be slate 100 and the dark mode the border is gonna be slayed 700. great that's the outline button we probably forgot a comma at the end of the previous one great and then we also want to insert a comma right here and now for the ghost button goals the variant of this button is going to be ABG of transparent so at first you're not gonna be able to tell that this is a a regular button but it's gonna have all the best accessibility implementation so when you use keyboard navigation for example it's still recognized as a button it's just not visually immediately obvious because that's what we want to happen then in Hover a BG of slate 100 and dark mode and hover a background of slate-800 in dark mode the text is going to be slate 400 and if we have a data off and then in these array signs right here if we have a state of equal to open so State equal to open then the BG is going to be transparent as well if we're in dark mode and have a data Dash State oops state is equal to open then the BG is going to be transparent so no matter if we're in Dart mode or in light mode the background we're enforcing it to be transparent for this ghost button and then the last variant that we want to have is the link variant we can use for links this is also going to get a BG of trans transparent in dark mode the BG is going to be transparent as well we want an underline and the uh we want an underlying offset to be formed so that means when we underline this link the underline that we have is not going to be immediately below the text but it's going to have a little offset so it looks a bit prettier when we hover over this the underline should appear we want a text slate of 900 that's going to be the default text color pretty dark and then when we're in dark mode it's going to be the opposite it's going to be very light so in dark um the text is going to be Dash slate Dash 100 when we hover over this the the BG the background will stay transparent and when we're in dark mode and however the background will also be transparent great that was the exhausting part of the button done now we can move on with the sizes they're going to be a bit more enjoyable to write so size it's gonna have three different sizes one is going to be the default that we want to pass a height of 10 padding y of two and the padding X of four then the small variant is going to get a height of nine so just a bit less High Dash nine no comma there then a padding X of two and a rounded of medium and then the large variant of this button is gonna get a height of 11 padding X of 8 and also a rounded Dash medium and now let's finally Define the uh no not like that let's define the default variance so default variance gonna be very easy variant it's going to be default and then the size is also going to be the default size that we've defined right here and the default variant up here great and that's the button variance done now you already know the drill on the rest so we can just get the interface extend that and the reason we're doing that I expanded previously because first we want to get all the attributes that a button could normally get wherever we render this component so we can pass them on we do that by extending the interface with the button HTML attributes and then here we can pass the HTML button element like that and then again we also want to extend this by the variant props or the type of button variance so we can pass the size and the variant of this button wherever we render it great and then one final thing we want this button to have a is loading property which is going to be an optional Boolean value and we can pass to this button if we want to okay again we're going to do the forward ref thing so if we wanted to we could pass a ref to this button component from the parent wherever we render this and if we don't want to then that's totally fine as well that will work just fine the forward drive takes two arguments so first one being the HTML button element that we're trying to render out here and then second one being the button props that we've defined up here to let typescript know what the props are that we can receive um and this structure right here and we're going to destructure the oops the class name from this component we're also going to destruction the children the variance of the button the is loading custom property that we've implemented up here the size of the button and then everything else we will receive as the dot dot prop so that's a catch all for everything else that is passed and then as the second parameter argument we can receive the ref of type forwarded ref HTML button element but the type is automatically inferred and we don't need to worry about that and then to finalize our component we're going to render out the actual button and this button is going to get a class name of the CN utility function to conditionally merge these classes together of type button variants and then here whatever is passed as the props we're going to just pass in here so that's going to be the variant the size and then also the class name if we want to change that from wherever we render this button then we can have the ref in the button so a link to this Dom node of the button the disabled property is going to be is loading so whenever the button is loading it's also going to be disabled so the users can't click it again which would make no sense then we want to pass all the other props that we have received right into this button component and inside of the button we're going to have two things first it's going to be the children and then secondly what also makes sense to have in here is the is loading and if it is loading then we won't have a low oops a low Loader 2 that we get from Blue seed react and it's not showing the proper import right here so let's import this manually import Loader 2 from Lucid Dash react which is the beautiful icon we're going to use as the loader this is gonna get a class name of margin rate 2 height of 4 width of 4 and then an animate Dash spin so this loader spins by default that we get from Tailwind already which is super convenient and then else we're gonna render out no so if this is not loading then we're also not going to show any spinner great that's our button done last thing we want is the display name of this button and that is going to be equal to button just for debugging purposes if we ever need that and what we can do now is import these button variants that we by the way also need to export from the button component inside of our navbar like that then the variant should be not variants but variant we the theme toggle we still don't have and now it's going to be super easy to create the sign in and sign out button let's get started with the sign in button we're going to create that under components sign in button.tsx and that is going to be the link to the Google login which is going to be super convenient and instead of the sign in button first thing we're going to do is initialize this as a client component because we're going to use an on click Handler on the button and if we do this has to be a client component because server components cannot utilize that kind of interactivity then let's initialize this as an FC you already know how to create a snippet um and then here let's handle the sign in button logic so one thing we want to have inside of this button is a state so we can say state or if you don't have a snippet for that we can write the state ourselves it's going to be called it's loading and is loading and then set is loading it's going to be equal to use state which we can import from react and the default is going to be false now if you're in typescript we could explicitly type this out as Boolean so we couldn't change it later without it alerting us that any values that depend on this set right here would also need to be modified so I just like to do that okay then we're gonna create the actual button right down here and for that we're going to create um or use the super reusable button component that we've just done in the UI the button is going to say sign in and on click a function is going to be called that we're going to call sign in with Google and then is loading off is loading and there's going to be equal to so whenever this state is true we're going to pass it on as the is loading property to the button component now the sign in with Google function doesn't exist yet so we're going to say const sign in with Google is going to be equal to an async function that we're going to use to handle the sign in logic in the sign in button and for that we're going to use the set so first thing we do when we call this function right here I set the is loading to true and I am going to disable GitHub Co budget for this and because it is getting kind of annoying and then we want to have a try catch Block in here because we can handle some async logic so what we want to happen is to wait the sign in function that we get from next auth slash react it's super convenient and then here we can pass the Google provider because that's gonna that's what we're going to use to allow users to log into this application and if this goes wrong for some reason we want to have a toast notification that we're also going to create together toast and that toast is going to get a title of error signing in then it's gonna get a message of please try again later or anything that you want as a message and the type of this tools notification is going to be of type error and that is the sign in button done now we would be signed in to Google if we just had the toast notification but let's say that for now let's accept that it doesn't exist yet and import the sign in button and we don't want that from dot but from add slash components slash sign in button and same goes for the button variants that we want from add slash UI it's just a bit prettier having those as the Imports and then the sign alt button is going to be very similar and the reason we have these as oh by the way let's create a sign out button dot TSX and the reason we have these as separate components instead of just having one button with and you know passing at the function that it should execute is because you cannot render the client-side components from a server component like the nav bar and pass it a function directly that doesn't work so where the the only two unreasonable components or I guess they're not really unreusable but un like modifiable components are going to be these buttons right here so we're just going to copy paste the sign in button called sign alt button and we use that naming across this component so we want it right here we also want it for the props and that is going to be FC of sine out button props now the logic is going to be very similar slightly different however so instead of awaiting this sign in where you're gonna await this sign out that we also get from Next auth by default and then you know just change this to error signing oh please please try again later and also change this to sign out and we could also change the name of the function let's call this sign user out it's just a bit more fitting to what we're actually trying to do now for both of these buttons the toast doesn't exist yet but we're going to worry about creating that here in a second for now we can already import the sign out button right here from um add slash components slash sign out button great now there's two things missing currently that is the theme toggle and also the toast notification they're not too much work so I think we can get started on them right now now before we do that though I think it's always important to live see what you're even building instead of just typing along with what I'm typing so let's come with this theme toggle out for now and also these toast notifications and let's see what we have built so far let's just count them out in the layout the nav4 we still need to import that and that is going to give us an error because we are using the navbar as a server-side component that uses an async right here so we also by the way we can remove the props we are not going to get any props for the navbar um we need to expect this error the next team is currently working with typescript to resolve this but for now we need to say add TS expect Dash error and then server component now the server component is completely optional you don't have to have this in there but I just still do it so you know why we are expecting this error and if we save that and go to the browser like that we can see um what we're building in the localhost and that is not the application just yet let me see is the server started okay so the server isn't even started let's start up the server using yarn Dev or npm run Dev if you're using npm and that us restart the localhost 3000 and take a look at what we have built so far can close that down let it load give it a second and then this is our navbar great we can see on the right hand side there is the documentation and there is the sign in button that we can use and these actually already work at least the documentation now the sign in button doesn't work perfectly just yet because we haven't done the whole authentication but we're going to do that um but first let's finish up in navbar with the theme toggle um and I think that was all we need to do to finish up the navbar right so let's insert the theme toggle right here and that is going to allow us to switch between dark and light mode it's gonna look really really good and let's go into the theme toggle so let me move this a bit to the side again let's create a new component called theme toggle dot TSX initialize that as NFC and now we can get started building of the theme toggle now let's think about the functionality that this theme toggle would have to do and the most important thing is change this the the theme the underlying theme right and to do that we're going to import the set theme from use theme which is the hook we get from next Dash themes that is why we're using this package it allows us to change the whole theme of the application super easily with this simple hook now because we're using a hook that also means we have to use the client side rendering tactic for this theme toggle otherwise we couldn't use a hook now to build out this steam toggle we are using a Radix primitive so if I go to Radix drop down menu you can see that we're using the drop down menu from Radix which is an unstyled drop down menu that exposes a bunch of really handy stuff like the trigger the content the portal it handles all the logic that is needed for a drop down menu for us without dictating any of the styles however I think it would be very tedious for you to type this out yourself to do the whole drop down component so I suggest that you go over to the similarity I GitHub that I made and go into the source folder into the components the UI folder and then enter the drop down menu dot TSX and just copy these 200 lines of code right here that might seem a bit unintuitive at first but um don't worry if you understand exactly what is going on in this logic right here because essentially what we're doing in this logic um is the same thing that we've already done right the forward ref we're passing it a bit of types and it is just easier to copy and paste this if you're not familiar with typescript um you could rewrite this in JavaScript if you wanted to um but essentially we are getting the unstyled um stuff from Radix stating this or self with some Taiwan classes and I don't think it would be worth all time um to call this out yourself so just copy and paste this into our drop down menu that we are going to create right now inside of the UI folder call this drop down menu dot TSX and paste the file that we have just copied from GitHub now if we take a look at this we are importing for example the root and the trigger and the group all from Red X UI react drop down menu and then we can do whatever we want in styling this um but as I said earlier it's not really worth worrying about even if you don't understand exactly what is going on you're gonna see why we're doing this right now and that is to build this theme toggle right here it's going to be super easy using this component so because we just did this we can import the drop down menu from add slash UI slash drop down menu we can close all the other stuff by the way we don't need that right now it just clogs up the view a bit and the convenient thing is now we can work with this with the um first thing being the drop down menu trigger so um when we click this or the the children of the drop down menu trigger then the drop down menu is going to be activated and we also want to render this as child which is an option that Radix UI exposes to us um because we want to render a button in here so we can customize the style of the button if we didn't have this as child directive this would be a button already by default and then passing another button into the button would need would lead to a hydration error which we want to avoid the variant of this button is going to be ghost this this is the button we will find ourselves and the size is gonna be um small we don't want the steam toggle to be huge and then inside of here we want the Sun from Lucid react just the icon give that a class name of rotate Dash zero scale of 100 transition Dash all so we're preparing this to be animated really nicely if you take a look at the final product we can see that if we change the mode there's actually a little spin there it's a really really nice and smooth transition that's what we're doing with these classes right here if we hover over this we want the text to be slate 900 and if we are in dark mode we want the minus rotate Dash 90. so rotate this in a negative Direction by 90 degrees if we are in dark mode this scale is going to be zero and then also for in Dark mode the text is going to be slate 400 so that's going to be the text color and if we're in dark mode and hover over this element this icon and text is going to be slate 100. great then we don't only want the sun we also want the moon for the dark mode right it just makes sense so we're gonna import the moon also from Lucid react pass this a class name of absolute so we're gonna rip it out of the normal styling of CSS and put it and in an absolute position rotate it by 90 degrees with a scale of zero then a transition also every animation we apply to this is going to be smooth when we hover over this we want the text to be slayed 900 and when we are in dark mode then we want to rotate this to zero so rotate Dash zero when we're in dark mode we want the scale to be 100 when we're in dark mode we want the text to be slate 400 and then when we're in Dart mode last property that we're gonna pass I promise at least for this moon is going to be hover and text oops text Dash slate Dash 100. so let's take a look at the theme toggle for now we can just save this import the theme toggle now there is still a bit of stuff left for the steam toggle but as I said earlier I want you to see what we're building while we are building it let's yeah it doesn't do this uh let's move this over go to localhost 3000 and as you can see the theme toggles right here now it doesn't do too much yet if you click it but we can see the moon because we are in dark mode now as an accessibility best practice what we want to do is also include a span right here and then give this a class name of Sr Dash only what this means is screen reader only so it's essentially hidden but screen readers will be able to tell the contents of this and so for people who are visually impaired for example that need to have screen readers we're going to include this and say talk theme so that they can also navigate the website without any problems and that is the drop down menu trigger done that is this icon we've created up here and now we want the content right so what happens when we open this drop down menu for that we're going to render the drop down menu content it also comes from the component that we've just done the drop down menu and in here we're going to have a drop oops a drop down menu item from the same component okay the content should have two properties that is going to be a line and and also Force Mount used to force mounting when more control is needed useful when controlling animation which we want to do with react animation libraries exactly and then the item what are we gonna do in here it's gonna get two things first the Sun that we already imported from the seed react with a class name of margin rate of 2 a height of 4 and a width of four and that is going to be self-closing so whenever we click the Sun what should happen well it oops um whatever whenever we click the Sun what should happen it makes sense to set the theme set theme to light right when we click the Sun that should happen and let's include a span right here that says light just inform the users what they're about to click and then as the second menu item you can actually just copy paste this I think that saves us a bit of time we're going to do the same thing for dark obviously won't set the theme to dark mode and render the Moon instead of the Sun and then as the last menu item we're going to copy paste this again we're going to set the theme to system as the third option now this is going to be the laptop icon you can choose whatever icon you like and let's say system in here there we go and just like that we've built a super clean component that we can use to switch between light and dark mode now I think one reason why this doesn't work yet is because we also need to configure one more thing in our Tailwind config and that is for I'm not sure how this is called let me look this up Tailwind config it's called Dark mode like that and we're gonna pass that in Array that receives the class values that means we are using the class strategy to render or dark mode and that did not compile because typescript doesn't check this we also need to include a comma right here it's a bit in transparent okay so we have the dark mode s class in here and now let's try if it works and yes it does beautiful so now we can use the theme toggle to go between light and dark mode and the system and essentially what this does is let me show you what this class mode even does so when we go into the elements and zoom in a bit we can see that we're in dark mode right now and pay attention to this HTML right here if we go to light mode you can see the color scheme changes to light and there is also a light class applied to the HTML if we go back to dark that light changes to the dark class so that is how every component in our application or every div every button knows which mode we are currently in that's how Tailwind CSS Works under the hood and by the way while we are in this Tailwind config we can Define just a bit more stuff so for example we're going to extend the theme by a container so we're going to say container that is going to apply to The Container class that you already saw earlier we're going to say Center true we're going to say padding of 1.5 RAM and the reason we're doing this is just to unify the sliding across your application that is where we're configuring this container and then screens it's going to be an object where 2x XL is going to get a 1360 pixel value great and that is everything we're going to extend the container by and now let's extend the default Tailwind theme it's going to be at two properties it's not much first the font family we want to use the inter font so we're going to say sense response I don't know how to pronounce it but it's going to take an array that has a string in it with VAR and then dash dash font Dash inter to make sure we're using the enter font then spreading the font family dot Sans now where do we get this phone family from we get this from an import from the Tailwind default theme so we can say const family no that should be font family is going to be equal to require and then Tailwind CSS slash default theme there we go we can save that and then the second thing we want to extend our turbine config by is by colors so that is for the main gold color that you saw in the page when I show you that again it's this gold color right here we wanted to find that in our Tailwind config and we do that by spreading in all the default tail and colors and then we want the light Dash gold to have a value of hashtag and then F5 oops F5 bc51 as a hex value and also the dark Dash gold to have a hex value of hashtag 533519 Now the default colors we don't get um like that we also need to import those from the Tailwind colors so we're going to say as another import const colors is going to be equal to require and then Tailwind CSS slash colors great that's where we get the colors from now you can see a change from any type to import colors so we know it's working and then the last thing we're going to change in the television config is the plugins configuration we're going to use two plugins in this um project first one is going to be require Tailwind CSS animate and then the second one is going to be a require at Tailwind CSS slash typograph oops typography like that let me expand this a bit these are both development dependencies that we have not installed yet so once more let's open up the terminal let's pause the app for a second then say yarn add or npm install D for development dependency and then we want the Tailwind CSS animate paste that in here also at Tailwind CSS typography also paste it in here and install those as development dependencies now the reason we're installing those as development dependencies is because we don't need them in production they shouldn't be used in a production setting they are just um for local development okay and one reason we just installed those is so we can configure one more reusable UI component that we're going to use in a lot of places across our application and this is going to be the toast.tsx now the toast notifications if you don't know what those are are little notifications that notify you of errors or of successful changes or whatever they make for a way better user experience and to Leverage The Power of these toast components um I think it makes sense to go into the GitHub once more and I think one last time and go into the UI and then go to toast down here you can see this is a bunch of lines of code that it I don't think it makes too much sense writing this out herself and what we're doing is we are creating the base code from react.host and then extending that code with or custom toast notifications so I think it makes sense to go in here copy the soul code that handles the toast notifications for us and paste it into our toast.tsx component now there are two arrows right now first one being the icons but we can get rid of that and then the toast icon props to fix the icon Arrow we're gonna Define a separate component for the icons called just icons.tsx this component is not even going to be a function component it's just going to be an export const icons and that is going to be equal to an object and here we're going to export the Chevron left and we get all of these from Lucid react by the way but the Auto Import doesn't seem to work so we're going to say import Chevron left from Lucid Dash react we're gonna also want the Chevron right then the Sun the moon and the laptop and we're gonna switch those out to where we already use them in theme toggle we want to import all of those Chevron right the Sun the moon and the laptop from the seed react and then export these icons as default export default icons like that and now for the components in here we can prefix this with icon so if we decide to change the icon later we can do it in one place we're just going to make this a ton easier to manage and that is our toast notification error gone as well we can just save the toast and also enable toast notifications on our application we need to go into our root layout and Define the toaster in here so we can say Toaster import that from the component we just created that allows us to render the toasts in the root level of our application and then the position property is the only one we're going to pass to that you can see there's a bunch of options but we want to choose the bottom right but you can choose whatever you want you could go with the bottom center top left whatever you want but I prefer the bottom right approach there we go we can save that toaster and now we can use the toes notifications and I think it was in this sign in and the sign out buttons where we left it out earlier now we can uncomment that and import the toast and design in button and also in the design alt button comment this in again import the toast notification and let's see if it works let's save all the components go into a split screen mode and then to our Local Host I think this is not proper split screen there we go the server should still be running it is let's restart this and take a look at the application actually I can go just full screen that's a bit easier to see and we get a hydration error no never mind we don't get it Let me refresh again no we don't get it okay and now we can click on the sign in let's see what happens and great we did get forwarded now we didn't implement the authentication yet but this is where the toast notifications would appear so we can actually just mock this for now we can go into or navbar and let's say we clicked on the sign in button instead of awaiting the actual sign in let's just do a toast notification instead we're gonna change this back in a second just to check if the toast notifications are working and they are working great you could see the toaster notification there in the corner so let's move this back into the catch where it actually belongs and there we go now we made sure that those notifications are working great okay just to summarize until this point of the video we have made some massive progress we have implemented the light and dark mode toggle we've implemented toast notifications and a really good looking and fully responsive um navbar good work if you're following along good work and we are not going to slow down we're gonna plow right ahead we can close everything that we have until now let's get started on the main page and style that because that is a really important when users get to our application they want to see something beautiful and this page is going to be really straightforward the first thing that we want to do is import the method data I think it's called um type so we can say type metadata from and then in quotes next so that we can now Define the metadata for this page it's going to be metadata of type meta data that we have just imported that's going to be equal to an object and here there's a bunch of properties that we can pass and this is the beauty of typescript by the way we can see what we can pass to this but we only want to define the title as similarity API home and then the description of free and open dash Source text similarity API and if we save that we can see what happens instead of localhost 3000 it says similarity API home on our website that's what we just did and now let's get started in styling this page it's not going to be a huge component so this is pretty straightforward let's start with creating a div that is going to get a class name of position relative height of screen so that's going to be a 100 view height in regular CSS it's going to be a flex item Center just oops justify their Center and let's go into full screen for this for these class names then an overflow X of hidden okay now in here we're gonna have a div called container with a padding top of 32 to offset the nav bar height and then add some because remember the nav bars 20 um will be set a height of 20 and we offset that by 12 additional units then a Max width of 7xl MX Auto to put this in the center and then also with a full so it's always the maximum width that we've just defined right here and then a full height inside of this div comes one more div one last div I should say with a class name of height of four a gap of 6 Flex Flex column justify Dash start in the large screens that's going to be a Justified Center with an items of Center and on large devices we want the items to be at the start so large items Dash start so we're going to differentiate between the devices make this nicely responsive and in here we're going to render a large heading now the import doesn't seem to work I don't know why but we can import that ourself import large heading from at slash components slash UI slash large heading and that is not a named export but that should be a default like that we can access the large heading inside of here we're going to define the size of the setting which is going to be large then we want the class name to have a very specific class name that we're not going to reuse but this is going to make this gold and 3D what you can see right here that's the effect we're going for so we're going to call this three dash d which is a class we're going to Define here in a second text black and dark mode detects is going to be light Dash gold now for this 3D let's quickly go into our globals.css and do a quick extension and so we want to say dot dark dot three D so when we're in dark mode then the 3D should be a text shadow of 0 pixels 5 pixels down and then zero pixels I think it's uh to the side the last one and then of hashtag 533519 that's the hex value we're using for this and then for the normal 3D in light mode we're going to apply a text oops text Dash shadow of none don't want any in light mode there we go save that and now this class gets recognized and as the main heading for our application we're gonna say easily determine oops determine a little break here so this looks nicer text similarity let's take a look at what that looks like in split screen let's go to our Local Host okay great we've got the easily determined text similarity let me change this into a nicer split screen mode there we go and now we can continue with a paragraph that kind of explains what we're doing let's give this paragraph a class name of Max with XL and then large text Dash left inside of the paragraph we're going to say with the text or let's make this lowercase with the text similarity API you can easily determine the Sim oops the similarity let's format this between two pieces of text with a free let's insert a JavaScript space here so there's always going to be a space and that is not going to be affected by the layout because right now we're gonna include a link right here that we get from next link the href is going to be to slash login and in we also want to pass a class name to this link which is going to be underline underline offset 2. and then text Dash black and in dark mode the text is going to be lights gold great let's give this a bit more space that we can work with and inside of the link that we have just created we want to say API key and also a DOT after that so if we save that you can see on the right hand side we can see the text right here and then it linked to the login page if the user want to get their API key right away okay this already looks really good and it's fully responsive let's continue after the link we're gonna have the last thing for this component which is going to be a div that has a class name of relative with a full this is going to contain the image later a Mac with a Max width of large on large screens the max width is going to increase to 3XL on large screens it's going to be left one half um an aspect of square because the image we're going to provide to this is going to be a square on large screens the position is going to be absolute and that's it that's all the classms we're going to be applying to this div and inside of this div we are going to contain the image component from next image which is a really handy component that does the image optimization for us super convenient um in here we're going to pass the priority tag because this is going to be our largest content for paint and generally you want to pre-load that when using when when loading the website it's going to improve your core web vitals as well we're going to play a class name of image Dash Shadow which we're going to create in our globals.css a quality of 100 this should look great users should be able to see the full quality of this style of we do this in this double curly braces object fits of contain then a fill so it fills whatever parent it has which will be this diff right here that's why we gave this so many class names a source of Slash type writer dot PNG and then an ALT of type writer now if you don't know where this image is coming from it's also in the GitHub repository don't worry about that go into the GitHub repo and then should go back to the roots of the project you can go into the public folder and then see the typewriter.png I might scale this down for the final version because this is a huge image and I think it's not necessary to be this big so I might scale it down a bit but essentially it's the main picture that we're showing here on the home page so let's go back into the split screen like this and also put the image in the public folder so the source and then oh no that's not the source that's in public right here I'm gonna just grab the image off of the other project I've got open right here go into public and then drag over the typewriter copy it in here but you would download it from the GitHub repository and put it as typewriter.png in your public folder okay let's reload the localhost and now we can see that the image appears it's gonna be fully responsive depending on the kind of device you're in it's going to be downscaled automatically in quality so it always loads very fast which next stress does for us with this image component that we rendered it in super convenient and congratulations that's the homepage done first page of this project successfully done good work really really nice and now we can get started on the other pages so for example the dashboard or the documentation page depending on which one you want to complete first and first let's start with the documentation page I heard you screaming Josh let's start with the documentation page and that's fine we can go into the documentation and let's get started on that so currently when we click on documentation we get forward to a site that doesn't exist and we don't want that to happen so we want to create a new folder in the source app that is going to be called um let's have a route segment here so let's put this in these parentheses I think they're called docs so what this means is that it won't be reflected in the URL structure in the end so if we were to go to localhost 3000 slash docs nothing would happen but it is for internal management it looks a bit cleaner and here let's say documentation the folder so we created actual path for this the page.tsx that's going to contain the content for our documentation page so if you were to navigate to the documentation now this page would actually get rendered and now we can get started coding out this page and the root level of this page is going to be really simple actually we don't need any props so we can remove those from the functional component and then worry about the metadata first so again we're going to import The Meta data type like that from next now we can Define the metadata for this particular page that's why we're doing this export cons meta data as metadata type that we've imported and we're going to say as the title similarity API and then a pipe and documentation so the user knows where they currently are if they just see the tab then a description of free and open source text similarity API great that's the metadata done for the actual page we want to pass this div a class name of container maximum width of 7xl a margin X of Auto and a margin top of 12. inside of this div is going to be a div with a class name of flex Flex column item Center so if you assign the flex column property then the item Center in justify Center actually switch around so now um if we want to justify Center within normal Flex in the effects color mode we have to use the item Center and let's give it a small gap of six and inside here there's going to be a large um heading that for some reason it doesn't give us the import option but we know where to get it from because we created that by chatting and then from add slash column pops components slash UI slash large heading and I think we can actually leave out the components because we have just the UI and that is not to be a named import I did that wrong again but a default one okay and let's say making a request instead of this large heading let's save that see what happens and okay so this is kind of clipped to fix that let's create a layout for all pages in here layouts now we do get an error for this but this is going to be fixed in a second we want to export default function um layout from right here that receives an object with children and these children if you're working in typescript we also want to define the type off which are going to be react dot react node that's the type of the children we are receiving and this layout function is just going to return a section and this section receives one class name which is of padding top 20 to offset the height of the nav bar that's why we're having this padding and inside of here let's just render the children save that reload the page and now we can see making a request is being rendered out properly it's not clipped and looking just as it should okay and now let's go with the paragraph in here and say API slash V1 similarity because that's what the path for this endpoint is going to be save that and why is that being rendered out so large that shouldn't happen did I mess up the paragraph stidings and I don't know why it imported the paragraph from UI dial chatting but this should be from paragraph that's why it's looking so weird and now it's rendering the paragraph properly okay there we go and we want to have one last component for this page which is going to be the code to show and we will call that documentation tabs now this component doesn't exist yet so let's create it in the components folder new file documentation tabs.tsx let's initialize this as a functional component we can remove the props because this won't receive any props also get rid of this in turn save that and now we can import the documentation tabs right here now that won't show anything yet but we will be able to see that something will follow right here the documentation tabs okay now to get started with the documentation tab so let's move that over we don't need that right now let's go into the component and I think there's one more component we need to get from the GitHub but then that should really be it I believe and this is a very simple component doesn't add any new functionality that you don't know yet so I think it's fine to copy it over and we get it from the source directory navigate into there and then when we go into components and then UI you can see there is a component called tabs this is what we want um we Define two red X Primitives or rather we import Red X Primitives and then Define the studying ourselves in this component right here um and they are really beautiful tabs that we can then use so in the finished version these are the tabs that you can use they look great and that is what this component is for it's 55 lines of code that we don't need to write ourselves so we can go into the skithub repository it's a client component we can go back over to our documentation tabs and this is not where we're going to paste it instead we're going to paste it in the UI folder as a reusable tabs component so we can name this tabs.tsx paste the code in here and there is not even a redx UI that we still need to install because we've already installed the Red X UI slash react tabs that this uses under the hood and now we're golden with these tabs in place we can get started building out the documentation tabs and the layout of this is going to be super intuitive now that we've got the tabs component if we take one closer look we can see we've got we've got the tabs list there is a tabs trigger so that would be what is at the top that the user actually clicks and we've got the tabs content and that is it this really makes sense The Logical structure of this component and now let's use it in all documentation tabs so at the root level we want the tabs that we get from the UI tabs that is going to contain all the logic for this component inside of these tabs we're going to render 8 tabs list that we also get from the same component and this tabs list takes two things or however many you want to pass but for in our case that's going to be two things that's going to be the tab triggers so tabs trigger that we get from the same component again and we want to have two code Snippets right we want to have one for node.js and the second one should be for python now these are kind of complaining because typescript knows that they're still expecting a value and that value is called value and what is this value exactly what should we pass in there well we can Define the um default value we want at the tabs level at the top level and in our case that's going to be node.js so this would mean which code do we want to show by default and then we can give this an additional class name of a Max width of 2XL and a width of 4 meaning it's always filling out this Max width that we just gave it because that makes it look a bit better on the front end and then for the tabs trigger and here we can pass as the value node.js then for the python you can probably imagine what we're going to pass it's going to be the python there we go and then we want to render the content right this would be if I show you what this looks like and let's move this over into a split screen again and sometimes that's a bit unintuitive there we go we won't have this open on the side and these are the tabs that's what we just created we've got the node.js and the python however these don't do much yet and now we're going to create the content in each of these and the content is going to be in the tabs content as you might imagine now the value for this tabs content is going to be node.js and then we also want to create one for python so what should be shown if this tabs triggers active versus what should be shown when the node.js tabs trigger is active that's what we Define in this tabs content right here inside of here we're gonna do um some cool stuff so first off we're going to render a simple bar and um I think yeah that doesn't give us an Auto Import but we know where to get this because we did the dependency installation ourselves so we can import simple bar from we got a package for that simple bar react actually you know what we're gonna put that on hold I think it makes more sense if we go with the main code component first it's going to be shown for the documentation and then I'm going to show you how the symbol bar adds to that in a really beautiful fashion but just for your understanding I think it's easier if we first do the code component okay now this code component is a component that doesn't exist yet we still need to create that and we're going to create it right here in the components folder called that code.tsx and this is a component I've already done a tutorial on on my channel it's a super beautiful component to do animated code and we are gonna do what I went through in that video to create this beautiful animation effect that you can see um right here if we go over to node.js this animation right here that is being handled by the cold component and then everything else but the tab component okay let's recreate that right here locally first we're going to initialize this this is a functional component you know how to do this by now a typescript component I'm super helpful snipper to do that and then we want to add some stuff to the interface so Define the type of the props that we're going to receive for this code component first we want the code that is going to be passed as a string that is going to be the actual code that's rendered out then we want to know whether we want to show this component or not um so if we're on the node.js tab for example we don't want to show the python code then the language and that is going to be of type language now that is a type that we can import yeah I don't know why these Auto Imports are not working that is kind of not very helpful but we can import this type language from Prism react renderer that's where we can get this type from essentially meaning if I hover over this the um languages that are supported for the syntax highlighting that we're going to implement in this so there's a bunch of languages in here most notably JavaScript typescript and python that we will profit from and then we want two optional properties first one is going to be the animation delay of type number and then the second one is going to be whether we want this to be animated or not and obviously that it's going to be a Boolean and now that we've defined the code props up here we can or we already know which props you can receive in this component meaning we can destructure them very easily hitting control and spacebar to know which props we can destruction this component so that's going to be the code the language show animated and lastly the animation delay the code that we want to render out we're going to keep in a state um so let's actually let's write out the state ourselves it's I think it's easier for you to follow along we'll call this text and set text is going to be equal to use state and that is going to be the animated if we decide to pass that as the props and if we do decide that this code should be animated then we want to render nothing as the initial text and else we want to render the code so if it's not animated we can show the whole code right away and we also want to get the theme from our current like the the theme that is currently active in the application um to later use that for the styling of the highlighting and we do that you might know this already with the use theme hook that we've used previously and in here we can destructure the current active theme and let's call that epic application theme like that and format that using prettier and great and let's get started on the logic for the actual um code animation and to do that we are going to use a use effect and by the way because we're using these hooks you state use effect we need to declare this component as a client component in order to allow for that interactivity this use effect we are going to initialize with an empty dependency array we're going to add to that here in a second and then if we want to show this component and the component is animated then the following is going to happen right if we don't want it animated we don't bother with the animation implementation that we're going to do right now first off we're going to have an index so let I is equal to zero and then we're going to set a timeout the timeout receives a callback function and either if we pass an animation delay we want to delay we want to delay the timeout with that animation delay and if we did not pass an animation delay let's just say we initialize that as 150 milliseconds okay after that timeout or inside of the timeout we're going to Define an interval so let's say cons interval ID and assign it to a set interval function that also receives a callback function and that interval we're going to delay by 15 milliseconds that I found very appropriate for the speed of code animation so it doesn't take too long the users don't get bored but it's also not too fast and so they still see the entire animation clearly inside of this interval we want to add to the text that is currently shown incrementally right so we're going to say set text to code dot slice so we're taking a part of the code that is passed into this component and we're starting at zero and going until I so the index so we're incrementing through every letter of the code and then animating that letter adding to the state that's where we're also going to increment the iterator we called I right here and then also if I is larger than code dot length obviously the animation is done and we're going to say clear interval and pass in the in interval ID and also this is going to be the animation we want to clean up after ourselves if we are using with an interval inside of a use effect we want to clean that up if the component unmounts because else we might get a memory leak because this would still be executed even if the component is not even there in the Dom anymore right and so to clean this up we're going to return a clear in interval of the interval ID from the user effect so whenever this component unmounts this clear interval gets run and we can prevent a memory leak that way great now to the dependency array we need to add some stuff so everything that comes from outside is generally going into the dependency array if you don't know what the dependency array does it re-runs this user fact function every time a value in the dependency array changes inside of here we want the code the show The Animated and the animation delay so outside properties that we're using instead of the um use effect and that is it now we can get almost to writing the jsx before we want to calculate the number of lines to dynamically determine the height this code should have to do that we're going to say cons lines is equal to text Dot split remember text is the state that we're keeping inside of this component now to determine the number of lines we're going to split this text at a regular expression and to create a regular expression we are going to create a two slashes instead of here it's going to think we want to comment but if we type A backslash it already recognizes that it's not a comment and then for this regex we want to split the lines at every occurrence that could be a new line right and to do this we're going to pass the r as the first for the regex then a backslash and then an N then a pipe operator the one you also use for the or in JavaScript a backslash and then r or a backslash and then n that is the regular expression we're going to use so at every RN or r or just n the N standing for new line and the r being a common symbol that is also showed it associated with a new line so um either this or that or this n we're going to split at to determine the number of lines and then const theme so the theme we want to use for our highlight component it depends on whether the state of the application is in dark or in light mode right and the steam is going to be equal to and the if if the application theme is equal to light then we're going to render out this as the light theme and else as the Dark theme now where do these come from these are Imports we can make from the or yeah from the library that we also get the syntax highlighting provided by the prism react renderer and we import them from separate modules so we can say import and then let's call this dark theme from and then here we can say prism react render slash themes and then the Dark theme specifically is coming from Knight owl and vs code is really slow again so let's just say Night Owl in here and then also import the light theme from Prism react renderer themes same thing again and then there's a theme called Knight all light that we're going to use for this I can close that down it's a bit easier to see the code and now we have the torque and the light theme imported from Prism react renderer that we can then use for the code down here now um let's render the Highlight component that we also get from Prism break renderer that is essentially going to do the syntax highlighting for the code for us and let's spread him some default props that we also get from Prism react renderer the code that we want to show in this component is going to be the text that we're keeping track of in state the language is going to be the language we are passing in as props to this component and then one last thing the current active theme for this should be the theme that we have determined right up here okay great and inside of the side light component we can benefit from a lot of stuff that it provides to us namely we can destructure some stuff like the class name The Tokens The get line props function and also the get token props function and then return some GSX from that in our case that's going to be a pre because we're rendering code and you know we want to follow accessibility best practices and do semantically correct HTML with this pre element right here that we use to render code now if you want to get into this component this this code component in detail I did a full video on this explaining you how to build this um but nevertheless I don't want to just copy paste this I want to build this all together with you so let's give this pre a class name and that class name is going to be whatever class name we already got plus a bunch of properties like the transition all to make this animated smoothly we want a w of it a background of transparent a duration of 100 no padding on the y-axis and a no scroll bar directive added to it okay and then we are going to imply some Dynamic styling to this using the style attribute we get from react and the Styles we want to apply are the max height this is going to be if we want to show this component then the lines times 24 pixels for each line and if we don't want to show this component then this is going to be 0 and this is throwing an error and the reason being that this is of type string array right now but we only want the length of how many lines we have and that fixes the issue and then the opacity if we want to show this component then the opacity should obviously be one and if we don't want to show the component then the components will not be shown with zero opacity great inside of the pre let's map through every token tokens dot map and if you don't know exactly what these tokens are don't worry about it it's kind of some abstract stuff that I don't fully understand either it's something you can easily get from their documentation so the point of being a good coder is not to understand everything perfectly but to know how to use documentation um well because if you try to understand everything it's it's kind of impossible to do right you just have to know how to read documentation well and apply it to your own projects so but that's besides the point let's go through every line and then every index and render out some jsx and for this line to work that we're going to write right now the cons key and the rest that we get from the get line props we can call that with the line and the key is going to be the index of what we're currently rendering for this line to work this is going to throw an eslint error specifically because we don't use the key value however we need to destructure the key value to not be used in the following components to prevent a react error so we're going to say yes lint disable next line no unused virus because we're not using that key as I just said and then we can return from this token sort map a component with a div with a key off let's just say oops let's just set the key is a template string it's called lion Dash and then the index and that div is also going to get a style that we can put in the Excel with the style we get from react with a position of relative there we go that's almost all we need to pass to this div one more thing we're just going to spread in the rest of the properties that we get from get line props and that's things like determining the styling of a current line or letter that we don't need to worry about because we're letting this package do it for us and inside of this diff and we're going to do one more map and that is through every line so line dot map and then for each line we get a token essentially like a letter then an index that we want to get and don't worry we're almost done from this we're gonna it's like two or three lines of code um in this we're gonna return a function so not jsx just yet because we're also going to do the same thing with eslint disable next line and we can just copy and paste it from above the no unused vars because we're going to do the same thing and destruction some stuff from get token props that we also get right up here and we structured at the beginning so we can say get token props and the get token props if we take a look at what this takes it's a token class name key or Style in our case we're going to pass the token and then also I and we can destructure from that the key That We're Not Gonna use and also whatever is left using the dot rest operator and then to get the squiggly red lines the way we need to return something and in our case that's going to be a span the span is going to have a key of the index then inside of the span we want to spread in all the props that we get and yeah not to get a naming error here we can call this crops instead of rest I think that makes a bit more sense and that is the token component done completely done with animated code and while this was a bit of a hassle to write I'm sure it's not the most intuitive thing to write this and that is totally understandable I feel the same way it makes for a super convenient code component that we can use in the parent component so for the code that we want to render um it takes a oh first we probably need to import this code component and let's not do this from dot slash code but rather from add slash components slash code this takes a bunch of properties as we know it takes the code language show animated and some optional properties the language we already know is Javascript or typescript doesn't really matter let's go with JavaScript then the code we still need to pass and what is the code gonna be well let's create a new file for that that only contains the code inside of or main directory The Source directory let's create a folder called helpers you could also also call this constants if you wanted to put this under constants both approaches are perfectly valid and let's call this documentation Dash code dot TS not DSX we're not going to render any jsx in here rather let's export the cons of node.js and this is going to be a template string and I don't think it makes much sense typing this out ourselves this doesn't really add to the video so let's go into the GitHub repository into source and then helpers if you call it the same um you want to paste it into helpers as well then into documentation Dash code.ts and let's copy and paste the code from here this is not actual code that goes into a project but rather one string that we want to be displayed on the documentation page later let's paste it in here and be careful indentation matters here so if I move this to the next line I'm going to show you what happens in a second when we render that out in our project right here and let's also get the python code that we need to display or want to display on our documentation page and Export that from here as python python could also call this python code that would also be good and paste it in here save that and now we can import the node.js code that we've just defined into our called component and then also we need to pass one more thing which is that we want to show this component yes and let's see what happens so let's go back to our page and if you take a look at this well the code is there but it's not animated so how do we animate it well it's as simple as passing the animated property to our code component let's restart this and as you can see the code is being animated beautifully and you might see yes it's clipping out a bit and that's what these simple bars are for but first we want to also get the python code working so let's do the exact same thing actually we can just copy and paste this JavaScript code if you're wondering how I just did that copy this line it's shift alt and arrow down and then if you let shift go and just hold alt in the arrow keys then you can move that line around if you wanted to so let's cut that paste it in the python adjust the language to Python and then also show the python code in here that we get from the same file and that is going to make both tabs work perfectly now for some reason the animated is gone so we want to add that back for this to be animated and then the simple bars come into play so say you're on mobile right this looks pretty bad and that's what these simple bars are for so we can wrap this in a simple bar remember we got that from the simple bar react I noticed that my camera just starts um but we're back camera is working again okay where were we we passed the python code and now we're at the simple box right so let's add in the simple bars you might not notice any oops you might not notice any change and we are getting a client error so let's declare this documentation tabs as a client component by saying use client we don't want any Windows updates right now and the reason we can do this easily is because remember we're running rendering this client component inside of a um sort of a server component now this robot doesn't look insanely good and the way we fix that is by also importing this CSS we need for this we could either import this in the page or in the documentation tabs let's import this here so we're going to say import and then the Sim oops simple bar Dash react slash this for distributable and then simple bar dot Min for minified.css and click save and hopefully if I refresh the page that simple bar is looking a lot better now the color is not perfect yet we're going to fix that right now but you can see for mobile this is way prettier um so people on mobile devices can also swipe to the left to see all the code that is inside of here now let's do one quick thing and that is adjusting the styles of the scroll bar right now we're in dark mode and it's black so it's kind of hard to see it so let's say that for the the dot dark and then dot simple bar Dash uh scroll bar and then double colon before because that's how we target the style of this we can say background Dash color is gonna be white except that and now if we reload the page the scroll bar should be white when we are in dark mode and uh it's not because with oops screw scroll bar because there's a little typo there and now as you can see the scroll bar is right it looks way clearer on dark mode to the users and that they can also choose this option to go to the right hand side of this code great I think that is the documentation page done we can switch between node.js and python we can check this out on full screen switch between light and dark mode and the code will always look good really really good work and I'm happy and we got to this point of the application great okay so a quick summary so far we've done the home page the home page is fully responsive um if we take a look at this the image goes with the page it looks really nice everything is available in light and dark mode with a beautiful animation here in the toggle and we've got a working documentation page also in light and dark mode I think it makes sense to tackle the authentication next because right now if we click the sign in nothing happens and that shouldn't be the case right and for authentication we're going to use the next auth and the easiest way to get started with next auth is by visiting their documentation page so let's go to the next auth documentation and see how we can best get started with next auth so we can go into the documentation section let's make this full screen for now and click on getting started and the easiest way to get started is you can see the API rot right here creating the API rod and then next authjs will worry about everything that is centered at API Rod so let's do that in our application let's go into the Pages directory source and then we want to make a Pages directory it doesn't exist yet we're going to create that and in here we're going to have the API and then if you take a look at the path right now next should be off so let's create a folder called that auth and then a file that is a catch all syntax in these brackets saying dot dot dot next of dot TS and click enter and the only thing we're going to do in this file is say export default next off and then we're going to pass some auth options in here which don't exist yet we're going to make them ourselves and the reason we're doing this is because I feel like if you want to prepare libraries for your application that shouldn't be done in a route but rather in the lib folder I think that is I'm easier to wrap your head around if you want to maintain this project and we can import the next auth from next auth slash and that should not be slash next just next off and then I think that could also work with the next I'm not sure and then the auth options we want to get from the loop so let's create a folder in here called auth.ts and this is where we are going to Define all the authentication stuff we need to work with next auth to get started in this file let's let's export acorns called auth options and now to make sure the type is correct we can import a type called Next auth options that next off provides to us so this is to make sure that this next auth function gets the type it expects and this is just going to return an object it's going to be equal to an object and then here we're going to Define everything that we need so now this is going to complain because and there are some properties missing that we still need to Define in this file so if we take a look at what exactly the providers and adapter we can provide some optional stuff and we're going to own utilize some of them first one being the adapter this is to make Prisma or database work well with next auth so we're going to say Prisma adapter and then here pass or database now the database doesn't exist yet that's the interesting thing and I think the easiest way so this is the thing right auth and database are very closely linked together so I can't just walk you through the auth and then the database we kind of need to do both at the same time because there as I said very closely linked together so the easiest way to get started with Prisma is to say yarn Prisma in it or NPA I think it's npx Prisma in it that is going to put a Prisma file into or um project with a schema.prisma in which we can then link our database URL however for now we're not going to worry about the Prisma file itself but first we just want to export the database even if it's just a mock or it doesn't really work yet and to do that we're going to create a db.ts inside of our lib folder now the reason I'm calling this DB instead of Prisma is because it should be kind of database agnostic if you wanted to change this to mongodb later then you'd only need to change this file and not the whole naming Convention of your application now the code for this Prisma database is just a copy and paste from the documentation there is no point in writing this yourself it's kind of confusing and so we can go into the GitHub repository once again then into lib and then DB and we can just copy this code I don't know like I didn't memorize this code either we can just copy paste this because of the documentation and it's all written out for us by the Prisma team essentially what this does is we check if there is a pretty smart client already and if there's not we're going to create a new one and if there is a cached version then we're returning that um cached version and that's it which means now that we've got that done we can close a lot of these files again like the documentation tabs and the page we don't need those for now we can import the DB inside of or auth.ts folder from dot slash DB and I think there is an import that we want to use for this so add slash lip slash DB then the Prisma adapter we also need to import and that comes from a dependency we've installed at the beginning of the video if you remember probably not called Prisma adapter and we get that from at next auth slash Prisma adapter um to make next all work well with our database now the second thing we're going to pass into this is the session and the strategy we want to use for the session as you can see we can pass the strategy property in here A bunch of other stuff that we don't really want to worry about we can select between Json web tokens and database in our case we want Json web tokens and you might wonder Josh why are we doing this the reason being that if we select Json web tokens one that extracts a lot of logic away from your database making it easier to scale because we're managing these sessions with a client and then secondly it allows us to validate the Json web tokens Integrity in middleware whereas with where we keep the session in the database we couldn't do that and we are going to need that to protect routes very easily and so I think JWT is definitely the way to go if you want to have an easy time um you know allowing certain routes and disallowing others depending on the user role jwts just make that way easier and then for the pages we want to define a custom Page by default it's always the you know slash API slash whatever if we click on sign in you can see um okay so it's the API auth error whatever we don't want those pages we want to overwrite them with custom nextges Pages the way we do that is by passing in our case the sign in and we want to map that path the slash login page that we're going to create it's going to be super simple page that just allows users to log in with their Google account and then for the providers we need to pass a comma here where the provide nope provide for providers there we go the only one we're gonna allow next auth to use is the Google provider so we can say Google Google provider and we also need to import that Google provider we can do that by saying import Google Provider from and then next Dash auth slash providers then slash Google you can see there's a bunch we could import like auth zero um Cognito from AWS but we want to use the Google provider there we go and the Google provider takes two properties that we can pass it like that and if vs code loaded I could show you this a bit easier but vs code is slow as hell sometimes it's the client ID and the client secret now to get these we're gonna create an environment variable or we already have a DOT EnV that we can use because Prisma created this for us um however so for your environment file I made it very easy for you to follow along you can go into the GitHub repository and then there is an dot env.example I've created for you you can just copy paste this mock.env that I've created for you into here and all important files that we're going to use throughout the application are in here for you so red is for rate limiting the open AI key a database URL the Google credentials we need and then also the next auth values just paste that into your dot EnV and we are good to go for now so for the Google provider for the client ID okay now it works we want to pass the process.env.google underscore client underscore ID that we get from the dot EnV file the Google ID right here and then the Google client Secret um I need to insert a comma here the client secret is going to be the process.nv dot Google client secret and now it says type string or undefined is not assignable to type string so it's quite meta does so um to make this a bit easier we're gonna create a function called get Google credentials and it's not really async function it's going to be super simple we're going to say cons essentially we can just copy paste this and put it here then put a cons before each of them and if you're wondering how I typed in two lines at the same time when you click server and then hold alt and click somewhere else you can type at two spots in your vs code it's super handy let's do that again oops they need to be at the same place and then it's going to be equal to process.env dot whatever these commas can be gone there we go and why is this still okay the function is initialized differently I try to mix up a constant function with a regular function and if there is no client ID or there is no client Secret then we're gonna or actually no that's not what we're going to do if there is no client ID or if the client ID dot length of the string is zero it's gonna equal triple equals zero then we're going to throw a new error and in here you can write what you want so for example no client ID for Google provider set so that you know in development that you forgot to pass that and same thing goes for client secret if there is no client secret or the client secret dot length is triple equal to zero then we're gonna throw a new error essentially we can just copy paste this um this is just an error for you anyways we can just say no client secret for Google provider set and otherwise if both are fine then we're going to return an object containing the client ID and also the client secret there we go and now instead of choosing these environment variables we can call the get Google credentials and then dot client ID and same thing goes for the client secret get Google credentials invoke that function and then client Secret so that means we always know when this gets rendered we have these values otherwise an error will be thrown first so we can be sure these are just strings we're passing to the Google provider great and then one for uh I think is it one final thing is almost one final thing one thing is we want to pass the callbacks so um we're going to extend our session async session like that it's a function that we can invoke and essentially when a session is created this function will be called from next auth and we can Define some custom handing in here we get values that you can destruction and use like the token and the session that we want in our case and we're gonna do some extension of our session so if we have a token if we see or take a look at the token value you can see it's a JWT that's what this token means if there is a JWT when this function is called then we're going to extend the session by the values of the JWT so we can say session dot user dot ID it's going to be equal to token dot ID the session dot actually we can just copy the session.user a bunch of times session.user session.uses session of user the dot name is going to be equal to the total token dot name the session.user.email is going to be equal to the token.email and the session.user dot image is going to be equal to the Token dot image now you might notice this throws a bunch of errors and that is because we haven't defined a type for or next auth so because we're working in typescript we need to tell typescript what are the types for next auth that we would like to be using and we're going to do that in a separate folder let's call that types and here let's create a typescript definition file by saying next dash off dot d for definitions dot TS so this means there is no logic in this file it's literally only types and what we want to pass in here is the following first off we want to disable the eslint unused voice because this will keep throwing an error if you don't um so yes lint Dash this a bull Dash or actually no no dash is then disable and then no on you unused Dash voice so this means we're disabling instant for the whole file and not just for one line and then we're going to import two things the type session and also the type user because these both are types we are fine just saying the type before the actual import um from next Dash auth and then lastly we want to import the type of JWT from next Dash auth slash JWT great and now let's Define the types so the type of user ID that we want is going to be a string and then let's declare the module of next of JWT first so um we're gonna say d clear module and then call it the next death of JWT essentially defining the global scope or Global types for this particular module that we're declaring right here the typescript always knows and always falls back to so in here we're going to say interface JWT extending that by an ID user ID so the JWT that you just saw um right here we're changing that right now and then also we want to declare a module next off in general and that is going to contain an interface called session and that session has a user property that we want which is of type user and also a ID which is of type user ID and I spelled that wrong declare there we go so now on the session we also have a user which we have extended by the regular user that we imported up here and then also added an ID property of type string to it great so you can see a lot of the errors went away there's still one error not 100 sure but we're not going to worry about it for now and then return the session from the async session function okay I think the reason is because this is called picture yeah it's called picture it's not called image but on the session object we are calling it image you could call it picture that's fine too I prefer to call it image and not picture great now for the JWT async JWT by the way if you're wondering how I came up with this I didn't this is just according to the next auth documentation if you want to read up on anything that we're doing right here it's all explained in the next auth documentation super well um if you get confused on why we're doing any of this for the JWT callback we can destruct the token and the user that we then want to modify so in this callback we can say const DB user to check if this user already exists in the database and this is the first time in the whole application that we're making use of Prisma as our database so we can say oh wait that's why this async this wouldn't even need to be marked async a weight and then DB that we can import dot yeah okay and now we got the case that we don't have any Prisma types for now so probably it makes sense before we continue on the authentication to actually get the Prisma types done right it's going to schema.prisma and actually create some models for our database now we don't need to think of all um all of these types ourself instead we can go to next auth and then search for next auth Prisma and they already provide a super good um schema that we can use for Prisma containing the user model the session model very verification token super handy and we're gonna base our Prisma schema on that so let's find that file on next auth.js documentation and paste it in here and that's already most of the work for a database regarding the the authentication done now we are going to extend this a bit while we're already here but most of this is going to stay just the same but since we're already here we can right away extend this by the API key logic that we want to have later so the API keys right how do they work well we're going to create one model called API key and one model called API request and for every request it is also mapped to which API key has made that request and the API keys are not deleted when we revoke them but rather they are disabled but not deleted that's a major difference so let's start with the API key model first this model is going to get an ID is going to be of type string adds ID that's how we Define it at the end Prisma then by default that's going to be a c u i d a collision resistant ID we want a key and it's going to be a string at unique so the this is going to be the API key itself that the user will see in the browser that should be a unique key always they cannot be duplicates then we want an enabled property that we can then later modify to disable or revoke a key that is going to be of type Boolean and by default this value is going to be true so an API key once generated should be always default unless we say otherwise then we want to make a reference to a user which is of type user at relation and the relation is going to get this field called user ID and then also we want to say that references the ID of user and then it might complain yeah because we also need to define the user ID of type string so we are determining which user does this particular API key belong to then the requests this API key has made requests there we go this of type API request array that's the model we're going to create right now and then each API key should be unique in the sense that it has a unique con like the the constellation of what we're passing right now the key and the enabled all three when taken together always are unique there could be a user ID and a key and those similar across multiple instances of this model but there is never a user ID and key and enable that has the same value of all three of those so that's what this unique means and then let's create a model for the API request so each request to our API will generate one of these models or one of these instances of the model model API request this is going to get some properties namely the ID of oh no that's not going to be a colon of type string this is going to be an ID and by default also a collision resistant ID yeah I know okay then the timestamp when was this request made we also want to save that that is of type date time and by default that is going to be now then a method so for example um pulse or put or patch or whatever in our case we only have one API Rod of type post but if you wanted to extend this super handy the path of the endpoints of Slash API V1 similarity for example this status or is going to be int so the HTTP status code for example 200 that gets returned the duration of this is going to be an integer then a used API key for this request is of type string needs to be uppercase so for each API request we're also storing which API key has made this particular request and then this might seem like a duplicate information to what we're doing right now because we also want to save the reference of the API key but I'm going to explain more why we do that here after we type this out that gets a field of API key ID and then it reference oops references the ID and to for that to work we also need to pass the API key ID of type string that is the value right here okay and because we can expect that that there will be a lot of API requests we also want to create an index so this query will be faster to the API requests and this index we're going to create on the API key ID and on the timestamp and there we go great for database model there's one more thing we want to keep track of and actually I think that is already working so one user has an API key that is correct however one user also has an API key ID which is of type A optional string and that is it great we can save that database schema and for this work we insert one more thing at the very top of this file that is the relation mode we want to use in modern Prisma we can pass that and that is of type Prisma because otherwise we wouldn't be able to use these form Keys now it does suggest this is outdated but I've I found this to be the working solution so I'm using this all the time I think it's a good solution for this and now that we've created this file we want to push it into our database however there is no database yet right and for the database we're going to be using something called Planet scale it's a cloud hosted MySQL service we're going to sign in if you haven't you can create an account in the service and they are using vtest under the hood it's a technology to handle a database scalability it works super well it's a fast database and I've had a really good experience using Planet scale now getting started with Planet scale is super simple you can log in using something like GitHub and then let's create a database together for in my case that's going to be in EU Central but obviously you would want to create it closest to you and let's call this similar similarity Dash YouTube and then click create database now before that's even done creating we can already get the connection string that we need to connect our database there are so many options in here like dotnet asp.net or Java whatever you wanted to use to connect to your database in our case obviously we're going to say that select Prisma for node.js and copy the string right here now I'm gonna obviously revoke this string after we've built this um but this is where you find your connection string while it's still initializing and then let's go back into our similarity YouTube Project and as you can see for the Prisma database we have linked the environment variable called database underscore URL and since we've defined the environment variables we can just paste it right here and then we can start pushing into our database let's say yarn Prisma DB push or npx Prisma DB push meaning we push those changes and we made in our database file up into the actual database and that did not seem to work because we also need to Define MySQL instead of postgres up here let's try that again clear the screen and then push that into our database and hopefully that will work just fine and now for our local development we want to run yarn Prisma generate Prisma if you don't is fully typesafe so it generates the types for us and for that to work we also need to generate the types after we pushed into our database and also we probably want to restart the typescript server to reflect those changes great we're making really good progress and now we can also use the features right here we've got on the auth.ts in our lib now we can actually find the user so we can say oops db.user dot find first invoke that and in Prisma we can find by a couple of options um we can say where and then you know there's a lot of options we could use but we want to find the first user of our application where the email of that user is the token dot email so whatever the JWT tokens that we receive here contains as an email and then by that we are finding out whether this user already exists or not and if there is no DB user if this user doesn't exist yet then we're going to say the token dot ID so we're extending the JWT is equal to the user and I'm using a um I think it's called exclamation mark to to let typescript know that we know this exists um dot ID and then we're going to return the token from this async JWT function and if there is a DB user if that user already exists we're going to return an ID of DB user.id a name of DB user.name so we're just forwarding the um the values of the user that already exists in our database the email of DB user dot picture or notes oh yeah we call it image right and then the picture wait now the email should be the image no no the email should be dbz.ima and then the picture should be DB user Dot image there we go we don't want to pass an image string as the email that would be pretty bad for Authentication and then one last thing three lines of code we're going to add to this file is the redirect redirect so after a user has logged in we can redirect them to whatever we return right here our case that's going to be the slash dashboard that we want to redirect users to great and that is the auth file fully done we can now import these auth options into our next author out now everything is coming together this is working nicely and um we could almost try this out one thing you'll notice is when we start up the localhost server let's move this over a bit I think first off we need to start the server again and then secondly we might only get so far as to getting a rejection by Google because they don't know um the credentials right so we don't have a Google client ID and client secret set yet and first off we're getting an invalid URL from the nav bar so let's take a look at that and work on fixing that right because we don't know where we need to get the service session from we also want to to pass the auth options that we have just created into this get service session so next auth knows okay this is where we're getting it from and now we're getting the error no client ID for Google provider set exactly what we want because actually we haven't set those credentials yet now the Google client ID and client secret getting those is really not very difficult we can go into the Google Cloud console and there are a billion good tutorials on this on YouTube let me log in here and here we are inside of the Google Cloud console now I've already got a project here but if you don't and chances are you don't you want to create a new project you can call it whatever you want I call it similarity API and then under apis and services we can go to login data now if you haven't created the project go ahead and do that it's super simple and then you want to click up here on create sign in data I think it's going to be called to create an oauth 2.0 client ID and within this oauth we want to configure um multiple things so for local development we want to create an authorized JavaScript source that is going to be for once the localhost that you're developing on and then secondly any deployment URLs that you want to use later on and the same goes for the authorized redirection Uris essentially whatever URL you want and then the slash API slash auth slash callbackslash Google because that is what Google is going to call for your application and that is all we need to do create a project get your oauth um sign in data if you don't know how to do that just create a new project like here up here new project then give it a name YouTube test we don't need to save it in any organization it's going to be creating the project and then once it has been created we can switch over to that by going up here and then into YouTube test now we are inside of this project and now we can go to apis and services and then log in data or sign in data and then create new silent data oauth client ID that's what we want we can close the notification first we need to configure the agreement like what the user sees when they try to log in with Google so on this right here we want to select externally so all people can see it we want to give it a name a support email and we don't want to create a logo because then I think we'd have to verify your application as you can see here on the right hand side this is what the screen looks like in the end right and then give it a domain the authorized domain and then contact data for you and that's all we need to do to create this and then you can do what I just said with the oauth client and get your Google client secret it's honestly super straightforward not hard to do if you follow just the steps that I just outlined for you and after we did that we can get the Google client ID and also the Google client secret paste them in here and I'm just going to use the ones I use for the actual application and we can also open Chrome here on the right hand side again so let me go into my actual application and just paste over the values now obviously I'm going to change them after the video um so it it would be best if you get your own values let me just pass them in here just so we get this working for the video and now because we change the environment variables we need to restart the server for the server to reflect those changes let's restart this and then we should actually be able to log into our application okay it says invalid URL again and the reason for this error is because we haven't set in the EnV the next auth secret and the next of your Elliott now the next auth secret is what we use to sign our JWT tokens and so you can enter anything in here anything my secret to sign JWT it should obviously be something way more secure than this so a random string that you generate probably not on the internet but on your local machine that would be best practice then for the next alt URL we're going to use our local development URL because this dot envious only in local development anyways it's going to be localhost at the Port of 3000 save that then once again start or restart the server and now that error should be gone because we have defined or next all of the URL and next auth should now know um how to get the service session and it does great now let's take a look at what our work has done let's click on the sign in button and we get forwarded to the Google sign in great and if you click this button we get forwarded or redirect it to the slash dashboard because that is what we have configured in the auth settings right here at the bottom returns slash dashboard however that route doesn't exist yet so it would probably make sense to next up configure that route and that's going to proper split screen here like that so it would probably make sense to configure that dashboard route next doesn't exist yet right so we can go into our source and then enter the app directory and let's create a new folder also for management let's put this in the um I think it's called parentheses let's call this dashboard it's just for organization this is not a URL change let's create a folder in there called dashboard and inside of that dashboard folder create a page.tsx which will mean that this page actually now exists and there is a page that is being rendered on the dashboard great that's a good first step and slowly but surely we're getting closer and closer to the Finish Line we have done a lot of the important work already the groundwork for the project so building this page in the following page is actually going to be much easier than if we hadn't so if you're at this point of the video congratulations and let's plow right ahead so for the page.tsx we are not going to receive any props we can remove all of that from the initial component and let's define the metadata first so import type meta data from next and then right below that let's define the metadata export cons metadata of type metadata that we've just imported from next it's going to be equal to an object and then you already know the drill how this works the title of the page this is going to be similarity API and then the pipe dashboard however feel free to name is whatever you want dashboard and then a comma there then on the next line we want the description which is going to be free and open source text similarity API period Oh No period doesn't matter great if we save that take a look at the page right now it says okay it already reflected the changes never mind but anyways that's great and then this page the logic for this page is going to be pretty simple we first wanted to find the user so to get the session you already know how this works is the await get service session that we get from next off super simple and then here we want to pass the auth options now for this to work as a weight we need to mark this pages asynchronous and let's just remove the FC because it will conflict with that and then if there's no user let's let's just for now render a P tag called No user we can still change that later and we want to return that and then or you know you could also have a not found error from next navigation right here and that would navigate the user to the this doesn't exist page I think this would be a better error handling actually let's leave it like that and the const API key is going to be equal to a weight then database dot API key the model that we've created dot find first now what is the key we want to find for user to determine if an API key is valid or not well first off we want to find the key where the user ID matches the user dot user dot ID so the user remember the one the the current session that is logged in and also the enabled property of true so we only want to get the key or the that is actually enabled to check whether this user has a valid API token or not then what we're going to render is going to have a class name of Max width of 7xl a margin X of Auto and a margin top of 16 kind of give this an offset of the nav bar and then on the page we're gonna do some logic so if there is an API key that means there is a valid API key because the type of this is either either API key that we get from Prisma client or null and if there is an API key then we're going to render something called API dashboard and if there is not an API key then we're going to render a component called request API here and that's it great now these components don't exist yet and I think it makes sense to get started with the request API key first because that is the easier component let's create that under components and then request oops request API key.tsx initialize this as a typescript component and we can also already do the same for the API dashboard just so we don't get the errors let's create the API dashboard dot TSX also initialize this as a typescript component and then import the API dashboard and there are no more errors great now there's no particular logic involved but at least the errors are gone let's close all the pages and then go into the request API key page and let's get started working on this page and because we laid all the important groundwork this um this this part of the website is going to be really enjoyable to build now this is not going to get any props either we can remove that and now let's get started and this is going to be a client component because we do want some interactivity in here so let's say use client up here and then Define some state so first one is is creating so if the user does not have an IP API key and set is creating by the way if the user does not have an IP API key then we want to create them1 and also get the loading State whether that is currently creating the API key or not and it's going to be equal to a use state that we get from react and there's going to be false by default and just to be just to make this a bit clearer we're going to also pass the explicit Boolean value for the is creating and then the API key that has been generated let's call it API key and set API key it's also going to be a state and that state is going to be either a string of the API key or null and by default obviously it's not going to be string but rather null great and now let us create the function to create a new API Cube so cons create new API key and it's going to be equal to an async function a constant function you could declare this as a normal function if you wanted to and then first thing we want to do is prevent the default Behavior because we're going to do this through a form element so we're going to get a form event like that as an event and there's going to be of an HTML form element generic that we can pass in the form element essentially just meaning that we can prevent the default Behavior which is to put the data in the URL we don't want that and it would also refresh the page we don't want that now to create a new API key first off let's set is creating the true because no matter whether it's successful or arrows um it's always going to be creating at least for a while then let's have a try catch block below that let's call this error or error it doesn't matter and then let's say const generated API key it's going to be equal to a weight create API key which is a function that we have not created yet then set the API key to that generated API key that we have just generated now this function doesn't exist yet but let's worry about the error handing first so if the error is an instance of error which is a global class that we always have access to um if error instance off error there we go then in that case we know that this error has a message that we can use for the toast notification so we can say title and then you know for example error then as a message for this toast we can pass the error dot or error Dot message we know that exists because we did the instance of type check up here then lastly we want to pass a type of error for this toast notification and return because otherwise if this is not of type error we don't really know what the type is then we're just going to pass a generic error message with a title of error a message of something went wrong because we don't really know what went wrong then a type of error as well great and then finally after all that is done we've got the cache right here and then finally no matter what happens whether we catch or they try successful we want to set is creating the false because at the end we're done creating this now this create API key doesn't exist yet and I think it makes sense to create this function right now okay so my camera went out again I hope it wasn't out for too long I don't really know um and to create this API key function we're going to create a new file in the helpers and then let's call this create Dash API Dash key dot TS great and then here let's export an async function create API key and what this function is going to do I think the naming can already kind of tell you what it's about to do we're going to create a new API key for a user and to do that we're going to fetch from an endpoint called create so cons response or res is going to be equal to await Fetch and then as an endpoint we're going to pass slash API slash API key slash create that's the endpoint we're going to use to create new API keys and then the const data we get from that is the awaited res dot Json and we want to infer that type or a cast That Type rather as create API data so whatever we return from that endpoint now that type doesn't exist yet let's go into the types folder and then we can create a new folder called like API and we can have an index dot d dot TS in there or dot TS doesn't really matter and let's define the create API data type describing what the API world will return to us as I type and we're going to do that by saying export interface create API data which is going to be an object and by the way the reason we're casting this as this type is so it's easier to work with later so we know what the response will look like with typescript that's the main benefit of typescript if you've never worked with it and to determine the shape of the response that we get back we Define it as a type right here that's why we're doing this that's also very important to explain this create API data will have two properties once the error which is going to be either a string or a Zod issue array we're going to get into sort in more detail in a second but first let's import that type zot issue from Zod which is a library to do schema validation we've installed at the beginning you're going to see exactly what that is here and when we create the API route so don't worry if you've never worked with the sword no problem at all and then null could also be true for there and then the created API key is either of type API key that we get from the Prisma client um so exactly what we have in our Prisma schema right the API key that we've defined as a model that could be the type of this or it could be null if we weren't successful in creating the API key and while we're already here we can also export an interface for one more API route and then that's it I promise the revoke API data endpoint and that also has an error which is either a string or a Zod issue array or that could also be no and then a success that is simply a Boolean either true or false and that's it for this file so we just described what the responses we get from the API endpoints that we're going to use to create and revoke API keys are going to look like if they are successful that means we can import the site right here and play right ahead with this function so if we have some error right we can say data dot error or the data dot created API key is not um true not truthy so if the API key generation failed then we're either gonna throw one error or the other and that depends on the shape of the error and to check the shape of the error we can say if the data dot error that we passed it on is in um instance of array so if the error is in Array then we're going to throw a new error of data dot error dot join that's an array method we can use essentially turning the array arrows into one single string that we throw as the error and otherwise if it's not an array then we're going to throw a new error with the data dot Arrow now this could also be no by the way if this were successful but typescript doesn't know that so if this is null or undefined then we're just going to say something went wrong and not worry about it further and that's the error handing done we need to obviously catch the summer in a try catch block but we are doing this right here this would be the catch block catching the arrows and then if everything was successful we can return the data dot created API key dot key so the value of the API key back to this generated API key right here that is calling this function great and now we've defined the entire function in our request API key and we can use that inside of the jsx to actually allow the users to request the API key themselves now doing this is quite straightforward we want to render out a div with a class name of container and then also on medium screens we want the max width to be 2XL so it can't exceed that on medium or up devices then one more div in here which is going to be of type Flex then a flex column so we list the items below one another a gap of six so we separate each item vertically by six units so that would be 24 pixels and so 6 units of 4 pixels and then the items Dash Center okay and inside of this div is now going to be a key icon that we get from Lucid react with a class name of mx-auto to be displayed in the center of the screen with a height of 12 and a width of 12. and the text of gray 400 so it's going to be this color below that we want to put a large heading and the Auto Imports still don't work maybe I misspelled something that could be the case I'm not too sure but we're gonna import the large heading from the add slash component or we can just go into UI directly and then large heading import the component we wrote ourselves and this is going to say request your API key now below that we want to render out a paragraph also and no this shouldn't come from large heading as well yeah I think I so when we go into the large heading there are why are there okay large so this is the only far right and this is called okay so why is the large heading called paragraph I think I messed something up it should be large heading I think we just copy and paste it over the code right this should be large heading as well this is already large heading heading variants okay so this looks better and now the paragraph did I mess that up to when we import that no that is from UI paragraph that works just fine now this paragraph is going to say you heaven and then in here we're going to write an and or a no that is yeah an and an a pause then a semicolon to simulate the I was 12 I'm not sure how that's called in English but this little letter right here can zoom in if you want the the the upper I don't know what it's called um because in HTML we need to escape that to be rendered properly otherwise it's going to throw an arrow during the build and when we've done that we can have a t re requested an API key yeah so you haven't requested an API key yet let's close that down inform the user that they haven't done that and then below that we're going to render out a form now that form is going to take an on submit so what do we happen what do we want to happen when this form gets submitted we want to create a new API key so we can copy that over pass that into the on submit of the form then this form is going to get a class name of margin top of six a small flex and small items Center so only on mobile is this not gonna apply and in action if we wanted to pass it as hashtag but we are preventing the action anyways because of the e dot prevent default right inside this form we're going to have one div like that and the stiff is going to get a class name of Relic oops relative rounded Dash medium Shadow Dash small and at small screens this is going to be a minimum width of zero and on small screens this is going to be a flex one so take up as much space as you possibly can that's what that means and then we're going to do some conditional logic in here if we have an API key if it was generated successfully we want to show a button that allows us to copy that API key into our clipboard right and if not then null so we're not going to render out anything in that case let's call this a copy oops copy button close that off and that copy person doesn't exist yet but let's pass it a class name already because we know that we will receive that class name is going to be absolute inset Dash y-0 if you don't know tell one very well this is the top and bottom property essentially as one it's super handy and then right is also going to be zero so we're positioning this at the right side of the input absolutely it's gonna animate in and also fade in that's why we have the Tailwind css-animate so we can use these it's gonna look really good then a duration of 300 for the animation great and then let's create the copy button really quick it's a very simple component let's create it here under copy button dot TSX initialize this essay typescript component you know the drill now this copy button and because we need some interactivity in it we're gonna have this as a use client um and then inside of this copy button we're going to extend the props I think you already know this from the beginning where we created the reusable components to allow any prop that a button could normally have to be passed into this so This extends the button Edge team attributes essentially meaning take everything from the normal button and also put it into here so we can then as you remember this means what the props can be right so we're saying the props can be anything that we defined customly inside of these object and brackets right here or everything that a button normally has and then here we want to pass the generic of HTML button element close that down for better readability great so now we can destructure um some props in here which would be the class name and then everything else as DOTA dot props again the catch all now there's one property we want to have in here and that is we need the value to copy right so let's call this value to copy which value should be copied to the clipboard that shouldn't even be optional we always want this value and we can also destructure that value to copy for this component and now we're going to render out the UI button that we have created ourselves this is super convenient the spawn component we're going to pass in all the props that we defined wherever we render this component then this is going to be of type button because we don't want this submit and the this button to act as a submit button in our form right by default it is but we don't want that to happen so let's just Define the time right here you could also have this as props obviously so you could say the type button in the well we still needed to import this right so let's say let's import this button and then say type is going to be button right here I think that's a bit cleaner to implement there we go and then the on click Handler right so how do we copy something into our clipboard in JavaScript the way we do that is through a function and then here we can say navigator dot clipboard dot write text and then the value that we want to copy into our clipboard obviously with the correct spelling and after we've copied that we can send out a toast notification saying title of copied a message of API key copied to clipboard or make this something we pass from the parent component which would probably also be a good idea and so the title and the message I'm just going to declare it here but I might change that indicator repo and then off type success because we successfully um copy to the clipboard then one property will we want to pass the button is a variant of ghost so this doesn't appear too and Visually like it it's not very obvious that this is a button and then a class name that very conditionally rendering with the CN utility function that we have created in the utilities with an empty string and then the class name as the second argument so honestly we could just actually leave this out and say the class name like that great and inside of here we're going to have an icon the copy icon we get from the seed react we're just going to have a class name of height of 5 and width of 5 and that's it now there seems to be one error right here and that is probably we didn't um handle the HTML correctly so let's say what's happened here so slash button there we go close that off correctly great and now this button looks good we don't need that anymore by the way if you're wondering how I remove the unused dependencies it's shift alt and all that's how you remove them in vs code and now this gives us an error because we also need the value to copy we're just gonna be um the API key that we have generated right so we can just copy the API key and have that as the value of the copy in or copy button really really good I think the last custom component for this entire project that we need to declare is the input so we want to render an input here however that input doesn't exist yet and I think once more it may or one last time it makes sense to go into the GitHub repo into the similarity API and then source into the components UI and then check out the input let me see I think yeah the table we're gonna write ourselves and yeah the table we're going to write ourselves so this is the last component that we're going to use from the GitHub repo it is pretty straightforward but um it is essentially just copy and pasting from Red X UI and then applying a bit of styling so not a huge value to type this out herself let's move this to the right side and then include that into our UI components so let's call this input.psx and paste the code from the GitHub repository and for the input and what the pacing allows us to do is just move a bit quicker through this whole project and the input we can now import from UI input let's have these as adds and then also the copy button would be add slash components slash copy button this should be add UI and this also add UI okay I'm back I took a little break so the lighting might look a bit different but let's continue where we left off and that is with this input component right here and we're almost done with the request API key component by the way so to this input we're going to add a read only because this is only going to display the API key and the user should not be able to enter any information themselves into this input then we want this to have a value of the API key or if that is null or undefined that's what these two question marks here mean then we're going to render out an empty string and by the way we don't want to render that input um conditionally but we want to move it below that conditional statement and then lastly for the input we can put a placeholder that says something along the lines of request and API key to display it here there we go and I think that is the entire component done great let me check one last time we've got the use client or we still haven't worked with the um is creating and the is creating goes into okay yeah we forgot one very little thing one Minor Detail that is the button component that actually submits this form let's wrap that button component in a div we're almost done though that's going to get a little margin on the top side a on the small devices it gets no margin though just on the mobile then on small devices we're adding a margin left of four and on small devices and up obviously we're adding a flex shrink of zero so it should not shrink and then let's pass a button component in here inside of the button component this is going to say request key so users can request the key and it's going to get a disabled of um like that and then double um I think it's called exclamation mark API key now what this does is it turns the string or null into a Boolean and I think we could not leave that away so this could be a string or no and essentially the um the exclamation point turns something into a Boolean but inverts it so we want to invert it twice to convert it back to the truthy value right and then this is going to get in it's loading as well and that is loading it's gonna say is creating so while it is creating we want the loading set of the button to show this button is not going to get in on click because remember this is inside of a form and when we click this button it's going to be a form submission by default so this function is going to get called with the request API key and we can check if this works let's go into the browser here on the right hand side we can close a lot of the stuff down and let's take a look yep this looks really good however this is kind of clipped so the way we fix that is let's move that over just a bit let's create a layout for this route so right now we're in the component no not in the components in the app and then in the dashboard and let's create a route for this dashboard so we need to expand this a bit create a new file in the dashboard folder layout dot TSX and inside of the layout we are just uh okay we're getting an arrow that's fine we are going to export default layout from this component layout there we go this layout is going to receive children and we can type the children out right now the children are going to be of type react dot react node so regular react components and then from this function we are going to export and this does not seem to work let's import that type from react import type react node from react like that and I think I messed up the syntax somewhere no that looks good let's return just an empty div for no no that I I definitely screwed up the oh I forgot the function keyword export default function layout there we go and now we can return a section from this layout with a class name of padding top 20 to offset the nav bar height so this looks just right and in here we're going to render out the children and that should prevent the clipping that you just saw happen here on the right hand side and it does really good and we have the request API okay the button looks a bit weird it looks too far to the left so let's just add a flex to this div and then a justify Center like that and let's see if that works yeah that moved the button to the center and let's see how that looks on desktop and on mobile that looks good great all right and now we are done with the request API key component we can close out of the dev tools and we can close this file and this file and this file and this file and this file and this as well and that as well okay so let's go into the dashboard let's see where we are at the page.ts X so we've got the request API key component for when somebody has no API key we don't have the functionality yet to check if this works now the question is do we either a Define the API dashboard for when somebody has an API key or first Define the actual API key logic so that we could request one right now and then see the other dashboard and I think that makes more sense I think we're going to implement the API functionality including the API routes to revoke and create an API key let's do that that makes more sense to do that we're going to go into our pages and let me see how I set up the routes here let's create a new folder called API Dash key instead of the API folder right that lives in the API not in the auth we don't want it in the auth and then inside of this API key folder we're going to have two files one is going to be create create dot TS and the other one going to be revoke but we're going to worry about that in a second we can maximize this because this is going to be back end work we're doing and not front end work and then let's handle creating an API key for the user so in all next.js API routes we're going to have a Handler which is going to be an async function and this Handler is going to be a default export export default enter like that and inside of the center in here we're getting two parameters for this function which is the rack and next API request and also the res in next AP oops API response that we can access within the Handler and there needs to be a comma there and we need to import this type and once again the vs code is super slow in loading this so I could manually import this but that wouldn't do much currently so let's say next API response like that but it's still uh it still needs a lot a bit so let's get started writing out the component and then worry about that later so we're going to initialize a try catch Block in this API world it's kind of a pain that I need to do this manually but whatever try catch error there we go set up a normal try catch block we can get rid of that and then for the try we want to get the user session first oh it's finally done loading great so we want to get the user session first right and the way we do that on the server you might already know this is by awaiting the get server session and no don't tell me it's loading again Jesus Christ man I swear vs code is like a slog sometimes um anyways this gets a recession is getting the request also the response and then also the auth options we can pass in there and I would really appreciate if it let me load that um so let's give it a second I'm gonna be right back when it's finished loading thank the Lord it is done and let's import the get server session and the auth options and let's attach a very quick and dirty dot then we get the response and then we would want to return the result user from that response so we get the user or undefined as or user and if there is no user then the we are going to return a rest dot status of 401 dot Json and with an error message saying unauthorized to perform this action and then SD created API key we're gonna put no by the way to enforce these standards in the response we can actually pass the types we have created remember we have created at types API index.tsd create API data that's what this is for so we can pass that as a generic to the next API response just pass it in there and also import that from the add types and why is that not working because we are not returning that in every instance that's totally fine we're going to work up to that now if there is a user we want to find out does this user have an existing API key I'll do this by saying const existing API key it's going to be equal to a weight and then DB we're going to import that dot API key dot find First and we want to find the first key where the user has this key and it is enabled so we can say where pass an object user ID is equal to user dot ID and then enabled should also be true so does this user have one valid API key and if there is an existing API key then we want to return an error right we cannot create an API key what we're trying to do in this route if the user already has one that doesn't make sense so we can return a res dot status 400 dot Json and in here we can say as an error you all oops already have a valid API key and then if I hit Ctrl spacebar you can see oh you can't see that well okay never mind I wanted to show you something cool but it didn't work and then SD created API key we want to return no just for forget it let's just continue on okay and now if there is a user and they don't have an API key yet um Jesus Christ go away okay if we have a user and they don't and they don't have an API key yet then we're gonna create one so cons created API key is going to be equal to a weight DB dot create why is that not working oh because we need to Define what we're creating first that's going to be in API key so DB dot API key dot create and we're going to create it with the following data a user ID of the current logged in user dot ID and then for the key we're going to use and I switched my keyboard accidentally and for the key we're going to use Nano ID that we still need to import and it doesn't give me the Auto Import so let's say at the very top of the file import nanoid from nanoid to generate that ID and I think I wasn't able to show you what I meant earlier is because the type import didn't work yeah so when when I hit control space where you can see what we still need to pass to satisfy this API response that is always of the type we have defined right here so essentially we're setting we're almost always going to return an error and an API key whether that's null or an actual API key so for the API key right here there's no because we didn't create one after all and then as the key for this API key we're going to invoke Nano ID but that doesn't seem to work is that a named import yes it is and now we have successfully created the API key and we can return that to the user so let's return a res dot status 200 successful dot Json that is going to contain an error of no and also the created API key that we've just made for this user great now the only thing that's left to do is handling an error and we can check if the error is an instance of Z dot Zod error um so let's import Z from Zod now in this case this would never be the case right but it would be if we did some schema validation with zot that's why we also want to implement this checking right here because you're going to see in the revoke Road that's exactly what we're doing and if you wanted to extend this API what it's just um you know um so both are equally created as you can also see in the types both have an error and that could be a Zod issue and that is just handy for expanding on this later but you're going to see exactly what I'm talking about when we get to the revoke route here in a second first off let's finish up the create API Key Road we're going to return a res dot status 400.json and then here we're going to pass an arrow of Arrow Dot and because we checked the type of the arrow we know what this will look like with typescript super handy we can pass the error.issues then the created API key is going to be no because we failed to create it and then if it is not an instance of a zot error it is some unknown error we're going to return a rest dot status of 500 dot Json and here we can pass the error as something very generic like internal server error and then the created API key that we promised to always pass because of the type is going to be no and there's one last thing um because right now someone could make any request to this route they could make a get request a post request put a delete patch whatever and this would always run we don't want that to happen so we're going to create one super helpful um utility function that is going to be called with methods so let's go into our lib folder and then create actually let's close all let's go into our lib folder then let's create a folder called API Dash middlewares and here we're going to create a folder called a file called with Dash methods dot TS this is going to be pretty simple essentially allowing us for every route to wrap this Handler in a with methods that we can then call pass it in Array of the methods we want to allow in this case that will be post and then pass the Handler essentially protecting this route from everything other than the requests we want to happen now to write this middleware it's rather simple let's export a function called with methods from this file that is going to get methods as a math actually no that's going to be a string array so either post put patch delete whatever the methods are and then also a Handler and we know the type of the Handler if we hover over this nope if we hover over that it's a next API Handler I'm not quite sure what didn't show that but we know the type it's in next API Handler and now we can actually handle the logic that should happen inside of this function so from this width methods we are going to return another function this is going to be asynchronous async function and that function is going to be an anonymous function so it's not going to have a name we're just going to create it like that with a rack and it's going to be next API request and then a res a next API response and you see the similarity here right the the Handler has this and this also has the same and inside of this Anonymous function if there is no request Dot and this vs code loading oh my it's loading once again I'm having a great time with vs code it's a lot of fun and I think the software vs code is based on is very good oh it worked okay so we still need to import these types and now we can check if the react. method does not exist or if the rack dot method no we're gonna type that or write it differently if the if not methods dot includes the rack dot method oops rack dot method then this is an invalid request and so for whatever methods we pass so for example the um the post we've passed right at the bottom here in our case for this April this would be just the post if the methods um did not include that post notification then that is invalid so we're going to return a rest dot status then 405 for this method is not allowed dot end and otherwise this is a legit request so we want to return actually let's do that before return the Handler and just pass on the rack and res and let's format that and once again vs code is back added and still loading so I'm not sure if I did um any type mistakes here I'm pretty sure I didn't but vs code wouldn't show it right now because it's still it's still loading let's give it a hot second and I'm gonna be right back when it's finished loading oh never mind there we go it just finished loading great okay let's save that and also we need to import the with methods in here and we are good to go that's the create route for an API key done and now we can actually create API Keys that's pretty dope and we can try that out in the browser right now so let's move this over to the side let's click on let's reload the page first let's click on request key and it says unexpected end of Json input let's see what happens can we see anything in the browser console um class 10 did not match now that's not what we're looking for uh I'm gonna dig into this and then be right back okay I think I figured it out this should not be a post request but a get request after all a post request to create an API key doesn't make too much sense if we're not even expecting data from that so let's see if that works and that did the work it generated an API key very very nice so we got that done and now we can close this down and we can close a bunch of files let's close all and let's see what we're going to do next so we've got the creation done now if we reload this page you should see that not the same page is getting re-rendered because in the dashboard component right here we can see if there is an API key then we're rendering the API dashboard and only if there's not we're running the rendering the page we were just on so right now we're running a completely different page the API dashboard so now I think it makes sense to actually finish up this project with the API dashboard and one last API wrote the actual functionality that we can request with the API key which is going to be the text similarity also a pretty simple API route so in terms of project progress I'd say we're 80 done and 20 left to go and that 20 is going to contain some really cool stuff that I think is really going to benefit your learning so let's continue with the API dashboard and this is going to be a pretty simple component with two new components that we haven't done yet but the rest is going to be very familiar to you so let's start this by declaring this as a server-side component we don't need all of this stuff this is going to be server side so let's mark this is asynchronous so that we can then work with it and the page.tsx throws an error that is completely fine we can just mark this as um double slash for a comment and then es lint or actually no it should be at TS expect arrows server components as I said earlier the they're still working to resolve this with a typescript team for now we need to mark this as CS expect error everything is fine with the component itself and there's nothing broken with it nothing wrong with it um we just need to keep that in mind and now let's get started on the API dashboard the reason we mark this as a server component is now we can request the user data so cons user is equal to weight get server session you already know the drill auth options and then if you don't have the user we can say not found just call that Arrow function when used in a reacts or a component this will set the status code to 404 when used in a custom abroad it will just send a 404 status so we're saying redirect to the nearest error page basically let's get the API keys for this user const API key so all API keys this user has ever had not only the current valid ones are going to be equal to a weight DB dot API key dot find many because we want all of them and then we want all of them exactly where the user ID is the user dot user dot ID great so now we have access to all the API keys this user has all um has ever had and we want to get the active one as well that's got to be const active API key it's going to be equal to API Keys dot find for each API key we want to check if API key dot enabled um because there's only going to be one matching that criteria we've made sure that is the case with Prisma unique so it couldn't happen that there are multiple enabled active API keys and if there is no active API key we are gonna invoke the nuts oops not found once again because if they're on this page that should definitely be the case right after all we they don't even get to the page if there isn't an active API key so this is just um you know for typescript basically to know that we can be sure there is an API key because typescript doesn't know about the other page right and then cons user requests you want to find out all requests this user has ever done with their API Keys it's going to be equal to a weight DB dot API requests dot find many and we want to match certain criteria being where and here we're going to pass the API key ID and then we're going to do something interesting we want to check every key this user has ever had right and we know the API keys this user has had so with Prisma we can say where API key ID in all the API keys this user has ever had we do that by saying in and then API Keys dot map and for each key we want to return the key dot ID essentially creating a an array of all the key IDs that this value should be in and then we have access to all the user requests and by the way if we take a look at the Prisma schema each user request also involves a timestamp and because we're about to pass this user this user request object or array down to another component we want to make this serializable because by default dates are not serializable meaning we can't pass them on so we're going to create a const serializer books and I changed my keyboard Again by accident serializable requests is going to be equal to userrequests.nap and for each request you're gonna return an object implicitly and that's why we have this syntax right here so right away return an object instead of having to write this return that we just can save this return statement by wrapping this in the parenth I think it's called parentheses right in the round brackets and then we're going to spread in the request but we're going to change the timestamp to make this serializable and for that we're going to import the format distance from date FNS again the import doesn't work no idea why import format distance from date Dash FNS that is a super convenient library to work with dates if you've never worked with it before essentially turning a Unix Epoch timestamp or whatever this is into a human readable format like one hour ago one minute ago and so on and here we're going to pass a new date um that is going to be initialized as direct Dot timestamp and then as a reference State we're going to pass the date right now and you're gonna see what that looks like in the final table and now the serializable requests we can actually pass on to the table component so render that out okay so for the jsx on the API dashboard it's going to be pretty familiar to you and most of the components the class name is going to be container then Flex Flex column and also a gap of six we want to give to this um to the elements in the flex and first one is going to be a large heading there we go you already know that one that is going to say welcome back and then we can get the user dot user dot name from the login data that we get from next auth just to greet the user you know it's always very nice and then pass a div in here with a class name of flex Flex column as well and medium that's going to be a flex row with a gap of four we're going to justify that content to the center and on medium devices and upwards we're going to justify that at the start and then also last thing we want the items to be in the center always great now inside of this stuff we're going to pass a paragraph you already know that as well paragraph as saying your API key and then colon and then after that we're going to pass the input you already know that as well that's going to be self-closing and as a class name this is going to get the width of it and then a truncate meaning that any text that goes beyond the input border will be replaced by dot dot dot and not look you know it just looks better this input is going to be of type read only as well and the value is going to be the active API key dot key dot key meaning the actual value otherwise we could pass like the ID the user ID whether it's enabled or not but we want the value of the key so we do that by saying dot key and then in here we want a couple of options in a second but let's just render that out for now and see what that looks like let's refresh the browser and that looks great welcome back wordful AI admin your API key and then it lists my API key down here and we want a couple of options for this API key we want to copy it revoke it and also generate a new one we're going to implement that in a second let's mark that as a comment for now um like right here and why is this one working there we go add options to create or revoke and that should be create new we're going to do that in a second first let's finish up this the the rest of this component with the paragraph right here another one paragraph saying your API history and this is where the table is going to come in this paragraph is going to get a class name of text Dash Center medium text left so on desktop devices we want the text to be left side with a margin top of 4 and move this to a minus margin bottom of four and just to offset this a little bit to make it look nicer to the eyes and maybe I can expand this just a little bit yeah there we go and now we can create the table the table looks super complicated but in reality it's a rather straightforward component if you know which libraries to use for it so the table is going to take actually let's just create the table first it doesn't exist yet so let's go into our source and then components let's create the table dot TSX component and the table is gonna allow for super cool functionality but obviously we're not going to do that ourselves that would be super much work but we're gonna rely on a package that allows us to do all that super intuitively for that package first we need to define the columns so let's say cons columns is going to be of type grid called Dev by the way this grid code f is not something we write ourselves this is a type we get from Material UI to import type grid call Dev that's just going to make it a bit easier to work with this we get that from Material UI from the data grid from at mui X data grid that's where we get that type from and that columns is going to be equal to an object now that is kind of metadata oh and this also needs to be an array and this is metados because it doesn't we're not returning anything so we're missing properties and we're going to add them right now so let's pass yeah that needs to be an array not an object and then we're going to pass an object as the first property with a field of column one then as the header name we're gonna say API key use that's going to be the column to list the API key that was used for this particular request that we're displaying to the user then a width of 400 and also a render header of params allowing us to make this bold and also add an emoji um which is not necessary I just found it cool um and then returning a strong so for strong bold html text that's what this semantic HTML is for with a class name of font semi bold to make this a bit Bolder and then we can say params dot call Dev dot header name so that is just the original header name say rename this column API keys that is what this value is going to be equal to and then we can also add an emoji at the end if that's what we wanted to do you could leave it out if you don't want it and that is totally fine then as the second column we're going to pass a field of chord to a header name of path and then a width of 250. and now we can just copy this down one two three times and then change each column subsequently to call three four and five and then change the second one to recency and a width of 250 that can stay the third one is going to be duration and a width of 150 because it's a very small number and the last one is going to be status and that is going to get a width of 150 pixels great however I intentionally named this column so you get the gist of it and but we still want to modify them just a bit because we also want each of them of these to be bold right however it would be kind of useless to include this render header for each of them if we could just map through them and do this for each so let's do this um and let's rename this as I think yeah columns draft so that's the initial draft of the columns and then for the actual columns let's say cons is going to be equal to and then columns draft dot map and for each call we are going to execute the following function so if um the call Dot field is triple equal to call one then nothing should happen right it's already bold because we've already included the render header for the First Column we want nothing to happen so we're just going to return the call early and if it's any of the other columns so one of these four then we're going to return um the call just spread it in but we want to make the header name bold right just like we did with the first one and so we can literally copy and paste the render header down here and the params will be of type grips grid column header params any any as a generic I don't know what this means I I mean I don't know honestly this is just the type you get from Material UI for some type safety we don't need to know what that means we don't need to work with it it doesn't really matter we can just uh fly with it as it is and that's the columns done great now we've got the columns for the table the rows will be supplied to us um by our database and now let's get to the actual component because we haven't even done that yet so let's initialize this as a functional component a table right down here we can move the import statement all the way to the top because that's where they usually go and then for the table props this takes the user requests right we want to pass the user requests from the API dashboard down to the table now the type of these user requests is going to be a bit tricky because remember we replaced the timestamp to be serializable right and we need to accommodate for that in this table component and the way we do that is with a little typescript trick we can Define the user requests with the requests as mod modified request type and pass that a generic of timestamp because that's the one we modified and that is going to be an array anyways now this type is not something we can import it's going to be one we Define ourselves right now um and this type of modified request type takes a generic I decided to name that K that extends to the key of AP oops key of API request now don't worry if this looks complicated for now essentially we're saying that this generic we decided to name k takes all the properties that an API request that we get from Prisma client normally has right so that would be all of these properties right here and now we want to change one of these so we can say omit which is a general utility function we get from typescript we don't even need to import that it's super useful it takes two generics first one being the API request and you can see what the function does here it constructs a type with a properties of T so the API request except for those in type K so what we're about to pass right now also named K and then an object that we want to modify and we want to modify the timestamp to be a string now this is a little bit of more advanced typescript don't worry if you couldn't fully understand it it's not a huge issue it's gonna appear very rarely in your react projects that you actually need to do this um it's just useful if you have to serialize the value and want to modify one type of that okay and now that we've done that we can get started with the actual table component which is not going to be super difficult because we rely on the library for the most part first we want to know the theme of our application so let's call it theme application no let's call it application theme and as you might know we get that from use theme that we get from next themes we can destruction that and that will be useful for determining the theme of the table so we can say cons dark or we can just say um theme is going to be create equal to create theme which is something we can import from and we can import this from system or material but we want to import this from Material mui slash material that's where we want to get the create theme from invoke that pass it a object and inside of here we can pass the palette option and that is going to take a mode off and if the application theme is equal to light then we want this to be light as well and otherwise we want this to be dark and there we go now one last thing we need to do is like before rendering the actual table from Material UI is determining the rows which is going to be super simple we can say rows is going to be equal to userrequests dot map and for each request we want to return implicitly the following object with an ID of request dot idea a column one and these need to match whatever we've defined up here in the columns by the way with a column one of request dot use API key because that's the API key like the the value that corresponds to that column for the second column we want to pass the request dot path for the third one call three we want to show the tab we want the table to show a template string containing the request dot timestamp that we've already formatted and then ego so this would be like one H or one M One S so for one second one minute one hour ago then for the fourth column we want to also pass a template string with a similar principle we want the request dot duration and then Ms for milliseconds and then lastly for the fifth column the status column that we created it's going to be the request.status and those are our rows now the actual data grid is going to be pretty straightforward first we're going to pass the theme provider that we get from mui slash and that should also be material yes we can import it from mui material with a theme of theme that we've determined right up here and close that off and by the way because we're using a provider there should be a client component use client like that because we're providing context and that can only be done in client components then in here let's render the data grid the big component data group with a style a little custom style of background color and then if the application theme is equal to light then we want the background color to be white and otherwise we want it to be of type hashtag one five two two three eight a hex value and we want a font size of one REM that's all the sliding we're going to apply to it we want a page size options of uh then this object notation then pass an array of five so five um listings per page and then the pagination will start you want to disable the row selection on click it's going to have an auto height so all the content is automatically shown and an initial State that's that gets past an object with a paginate pagination property Nation property with a pagination model that also takes an object with a page size of five now that was pretty quick you can take a look at that and also obviously in the materials your eye documentation I'm not making this up I swear it's also in the mui documentation now we also know we imported that great and the last thing this needs is the columns and the rows and since we've we've done all the all the preparing already we can just pass the columns and then the rows will be equal to the rows and we are golden great now that can be self-closing and now we can import the table from dot slash table and also pass that the user requests and that should be the serializable request and not the actual user requests and there we go let's refresh the page and see what happens if this works hopefully it does let's reload that and the server should still be up right yes the server is still up let's give it a hot second to reload and it shows the table beautiful so if you go into big mode you can see the API cues the path recency duration and status and then all the values um will be shown in here now we haven't done any requests yet but this is where the values will go okay just to summarize we're pretty far into the project we've got a beautiful light and dark mode we've got the API key functionality working it does generate we've got the table which is a huge important part we've got the home page done we've got the documentation page done and animated and mobile optimized already and we have got the dashboard both for when you already have an API key and for when you don't have an API key and the sign out functionality it does work what we haven't done yet is protected these sensitive routes that's what we need to do also we need to create one API rod for the actual servers right limit that API Rod which is going to be very straightforward don't worry about that and then we're almost done one thing we didn't do for the API dashboard yet is this add options to create new or revoke yet and let's add that right now and let's get that over with and let's create a component called API key options let's call it that for now and because we already got the drop down menu this is not going to be too hard as well so let's go into the components and create that API key options.tsx and create this as a functional component that we can then already import here no the drop down menu is going to come in really handy so let's render out a drop down menu from the UI you can close that for now and also come to this side view again like that's great and then inside of the drop down menu that we're creating right here we want a drop down menu trigger drop down menu trigger also from the UI we can say at slash UI slash drop down menu looks a bit better and the trigger is going to be disabled when we have set some state so inside of this component we want to initialize a state that is going to be called is creating new so are we currently creating a new API key for this user or not and that is a Boolean that is going to be false by default now remember because we're using functionality like State we also want to mark this as a client component and now and we also want one more set we can just copy this down and then call this is revoking and then set is revoking so are we currently revoking the API key of the current user which is also going to be a false Boolean and now this trigger should be disabled if one of these is true so we can say is creating new or is revoking if one of these is true the trigger will be disabled it just makes sense the user should not be able to click that when an action is already being processed in the background and then as child because we want to pass a button here and avoid the Dilemma of having two buttons inside of another and provoking a hydration error now this button is going to be a variant ghost and then have a class name of flex gap-2 and items Dash Center inside of this button we're going to have a P tag and do some quick and dirty nested a ternary statement um so if it's creating new is true then we're going to say inside of this button creating new key otherwise if it's revoking then we're going to say revoking key and if none of these are true we're going to say options so we're going to present the user with the options and that should be is creating you there we go NASA ternary statements are generally a normal but I think in this case it's really easy to read and it's not a huge deal and so we can go with it and then if is creating new or if is revoking then we can render something specific or else we can render null and what are we going to render a loader tool that we get from Lucid react as an icon and we're going to give that a class name of animate Dash spin height of 4 and width of 4. um so we are indicating visually to the user that the current state of the application is loading and then we can get to the content right we can begin with the drop down menu content that we also get from the same component and inside of here we want a drop down menu item drop down menu item also from the same component of course this item is going to receive an on click Handler on click and what that on click will do you'll see in a second when we worry about what should be in this item that is going to be copied so that's the text that is going to be inside of the item if we take a look at that in the um inside of the dashboard we need to sign in first to get to the dashboard let's do that right now and okay just I think it just hot reloaded while it was signing in um if we take a look at the options right now we see a copy right here that is what we're building and you can probably guess by now what this drop down menu item is going to do it's going to copy some text into the clipboard so the Navigator dot clipboard.right text is going to be called with the API key and I just called this API key it's kind of a weird naming but I think it's the most fitting considering how we name the API key because this API key options demands two things to be password that is the API key ID that we can then work with in revoking the API key also the API key you can name this differently if you would like okay so let's call the API key ID a string in the type of the API key key is also going to be a string the API key if you don't know what I'm talking about is just the literal API key that is being used to make the requests to the endpoint that's what that is great and now that we copied the API key we can let the user know we did that by emitting a toast notification with a title of copied a message of API key copied to clipboard and then a type of success oh yeah great then we want one more actually two more items but they're gonna look a bit easier drop down menu item like that and here we're going to say create new key and we can copy and paste that once more to say revoke key format that great and now we also need to pass the values we expect and the API key options from the API dashboard for the API key ID we're going to pass the active API key dot ID and then for the API key we're gonna pass the active API key dot key that's what we're going to pass to the API key options by the way we can close a bit of this stuff again and then let's see what we did so when we open up the options we can see copy create new key and revoke however these two don't do anything yet and it let's do the create first that's going to be super simple because we've already remember create API key we've already defined the utility function and now we can just use that inside of this component so let's create a function called create new API key const create new API key it's going to be an async arrow function and in here we're going to say set is oops is creating new to true because we always want to indicate a loading set and then in here goes a try catch block now inside of the try we're going to say away it's a revoke API key and then in here we're going to pass the key ID of API key ID now this revoke API key is also going to be a utility function we're going to create right after and then await create API key just call that utility function we've already created and then we're going to say a router that we get from next slash navigation we can say import router from next slash navigation and that is a why is that not working oh this needs to be the use router and then we're going to initialize the router as const router it's going to be equal to use router here in our component and with that router whenever we have revoked the API key and then created the API key we can then refresh the page so we can say router dot refresh and by doing that we avoid having to put this somewhere into State and then display it to the user again we can just refresh the window and do it that way then also have the API key at the API dashboard as a server component that doesn't need any state because of this segment right here then if the catch statement gets called then chances are something went wrong we want to let the user know by saying the title of error creating API key then a message of please try again later and then lastly we want to pass this as a type of error so the user knows what's going on and then fine oops no that goes uh yeah that goes right here finally you want to set is creating new false okay here we are back um it's a new day for me you probably didn't notice any uh change it's probably only been a couple seconds but for me it's a new day and we were in this file right here doing the revoke API key functionality and to do that let's go into the source folder and then we can find the create functionality and just create it in the same folder so let's call this revoke Dash API Dash key dot TS and inside of the revoke API key we're gonna write the function that is going to invalidate an API key for a user so let's export an async function from this file let's call it revoke API key now this function takes a key ID and we could write out the key ID not in this object syntax however when we do and then when we invoke the function we always know the parameters that get passed and I just prefer the syntax and because we only expect one argument in this function we can also Define the type in line doing this little object syntax right here where we have the object then a colon and then the same object with the same property and then the type of that property in our case that's going to be a string that we are expecting to be passed into this function and then we can say that the constress for response is equal to a wave Fetch and now we're going to fetch from the revoke endpoint and pass the key ID so the endpoint is going to be hosted at slash API slash API Dash key slash revoke and I think we created that together yes no we didn't yet okay but we are going to create it then in a second and then this is going to be a post request so for the method we're going to pass post because after all we also want to include the key ID right that we need to invalidate and then for the body of this post request we are going to pass Jason dot stringify of the key idea so we receive that on the server route and then for the headers one last thing we want to pass the content Dash type header which is going to be application slash Json data that we are sending um along to the API and that is the request done now we are going to get a response from that endpoint that we haven't defined yet but we can call this data and this data response will be the await res dot Json so whatever we get back as a response we're going to turn into Json and of that because if we take a look at this right now this this is any we don't know the type of this now we're going to cast that as a type again as error in this that is an optional string and there is a little syntax error here so we need to write as for that to work so either we get back in error or we don't get back an error and if we do we need to handle that so if we have a data dot error then we're gonna throw a new error with that data dot error like that there we go so we can catch that in the try catch where we revoke this API key specifically that would be in this sketch right here where we then render out a pulse notification and properly handle this error great let's go back into this file and save the revoke API key and now let's go back into the API key options and import this function and that we pass the key ID now everything works fine however the create new API key is never called and remember we want to call this um for the drop down menu item so we can pass an on click Handler to this drop down menu item saying that we want to create a new API key oops that function to be called whenever we click the drop down menu item and then for the revoke key we also want to pass an on collect Handler this one is going to be even simpler than the create new API key because we are just going to call the revoke route inside of a try catch let's call this revoke oops revoke current API key and also Define this function up here because we haven't done that yet let's do it below the create new API key let's call this revoke current API key it's also going to be an async function and um we're gonna also do this with a try catch so first off we want to set the is revoking state to True same as with the is creating and actually we can just copy paste this whole try catch block and paste it in here and then we still need to remember to change the state of is revoking let's set the set is revoking to true then we've got the whole try catch block but we don't need to create a new API key we can just await the revocation I'm not sure how that's called but we can just Evac await this revoke API key function and then refresh the router so the page will be reloaded and we save ourselves the hassle of handling this in state and I think we are almost done with this error revoking your API key instead of error creating your API key and yeah that works just fine and now we also need to set um the is revoking to false instead of the set is creating new to false and we are officially done with the API key options and let's take a look at what that looks like in the browser so let's go to or dashboard like that we are already signed in so we don't need to do that again and let's go to options and then revoke and see what happens it says revoking key and now it says error revoking key interesting so that shouldn't happen the server responds with the status of 404 not found okay yeah that makes sense so we did try to call the API route however that API route does not exist yet same as for the creating in API key so I think it makes sense logically to work on these API routes next to actually Define those and to do that we're going to go into source and then Pages add API then let's create a folder inside of API called view one now this is just a best practice in naming your apis after versions so even if you decide to make new versions of the API users can still use the Legacy the old versions of the API um it's just a good practice to do that and then let's create a create dot TS route inside of the API and we're going to do that at the API key and then the V1 is going to handle the actual functionality the similarity check that we want to do later anyways before we get into that um that folder creation was kind of unnecessary we're gonna worry about that later first we want to go into our API key directory and then here oh we've already yeah right we've got the creation Road and now we also want the Rev revocation I think it's called I'm not too sure the revoke so let's call this revoke dot TS because we're going to revoke an API key and once again vs code is super slow in creating this so yeah I'm just going to enjoy my coffee and let's give this a second and wait for this to finish loading great it has done the thing and now we can get started in this server-side API route it's called this Handler so const Handler is going to be equal to an async error function that we do like this and this Arrow function is always it's gonna get a rack as a type of next API request and then a res of type next API response and we need to import both of these from next and now we can get started with the actual logic of this API Handler however before we do that let's export this Handler as default Handler and also we want to wrap this Handler in the with methods middleware we have created with methods and don't tell me vs code is doing it again no do oh Jesus Christ okay let's give it another second to load and then the Auto Imports should work there we go okay now we can import you with methods invoke that pass it in Array and then we want to make a post request to this API route that should be the only one possible and then we can pass the Handler as the second argument into that function great so now we can only make post requests to this route and now we can handle the logic that should be applied to revoke an API key first off a user needs to be logged in so let's say const user is equal to a weight get server session and to the service session we can pass the request the response and the auth options that we have to find in the lib slash auth great and let's sneak in a quick and dirty dot then we get the response back and we want to return the res dot user if that exists obviously and then we either have a user or we don't and if we don't have a user then we want to not allow them access because they're not logged in after all in which case we're gonna return a res dot status of 401 unauthorized dot Json and in here we're going to say error unauthorized and then a success value of false now what we also want to do is pass this next API response a generic just like we did in the create route remember when I go back to the create API Rod we pass a generic of create API data and because we've already done the types we can see right here we've also got the revoke API data that we want to pass as a generic right here in the API Road and also import that but that doesn't work again so let's say import revoke API data from add slash types slash and I don't think we have a types route let's go into the TS config to check that if we have configured the types route and we did not do that so let's do that really quick let's say add slash types slash anything will map to um dot slash source and that needs to go into double quotes dot slash Source slash types and let's see if we can import the types from add slash types because this is an index.ts that might just work and let's restart the type server to check that and this needs to go into slash API as well so add slash type slash API and then it should be the index.d.ts slash index.d now I don't think we need that actually okay there we go I just deleted the line and now the Imports work for some reason I don't know sometimes um why is this not working now cannot find Mother bro you literally you automatically imported it from there and now you're telling me it doesn't work oh probably because we forgot the slash star in the tsconfig.json so if you're going to type slash API we also want that to map to the corresponding file path in or project and I really hope that will work now yes it did great okay we got that to work we've got the revoke API data right here in our Handler and now we can continue so we have a user right we've got the guard Clause right here that checks if you don't have a user now we can assume that we have users and so now we can find the existing API key for this user or we could call it valid API key is equal to a weight DB dots API key and we want to find the first API key where and you probably know what we're going to do right now where the user ID is equal to the user.id that is currently logged in and then the enabled property is true that's all that's what we want to check for and if there is no valid API key for this specific user that is currently logged in then we want to return a res dot status of 500 dot Json and let the user know as an error message now we get type safety by the way if you look at this and because we passed the revoke API data as a generic to the next response we can see which values we need to return from this Handler which is going to be the arrow and this is going to say um this API key could not be revoked and then as a success property that we also need to pass obviously we are not going to pass through we are going to pass false as the success and now if we are through these guard Clauses we know the user is logged in and they have a valid API key in which case we can actually revoke that API key that's invalidate API key like that and let's handle the actual Logic for that so that's going to be a weight database dot API key dot update and we want to update the API key where the user or the ID of the API key is the valid API key dot ID and then what exactly do we want to update for this API key that's going to be the data of enabled and we want to set that to false therefore invalidating the API key that the user currently has because remember we're only checking for the enable true property when it comes to a valid API key and now everything was successful we can return a status of 200.json and then here we can say that the error should be no and the success value should be true to let the users know that this request was successful now because we wrap this in a no we did not do that let's wrap this entire thing in a try catch because we're handling async logic we should definitely wrap this whole thing let's cut it initialize a try catch block and paste this inside of the try and if there's an error at any point now we can handle it right here in the catch part and let's handle the the arrow the same way we did in the create route by first checking if the error is an instance of Z dot zot error and to check that we also need to import the Z from Zod let's go up to the top of the page and import that import Z from Zod and now we can check if the error is an instance of a specific sort error and if it is then we want to pass or return a res dot status of 400 dot Json telling the user with the error property what the problem is and we are not going to write this ourselves but we're gonna get this from Zod that is the handy thing so we can say error Dot and now because we did the type checking we know this is a result error and we can assume these properties exist so we can say the error.issues and as a success property we are going to pass false obviously because we're in the catch block and error occurred and then if this is not a zot error if it's an unknown error of some sort then we are going to return eres dot status of 500 dot Json telling the users as an error message that there was an internal server error and then as a success property on this error we want to pass false because the request was unsuccessful great and that is or revoke route of the API done we can save that and now we can try to revoke this API key let's set the button it says revoking API key and yes sir it worked we're on the other page because we refresh the router and now we are on the same page as if we never had any API key in the first place great work if you're following along and are at this point as well really really good work we've gotten really far and I think the only thing that is left to do is handling the actual API key logic when someone makes a request to our main API key route okay and I just noticed one thing that is not really necessary that we're doing by the way when we are going into the revoke API key.ts route we are passing the API key ID to this endpoint even though we don't even need to do that so technically we are not receiving the key ID anyways and I think I'm gonna change that in the GitHub repository because this is totally unnecessary passing that and that is just one thing that I forgot in this code um so technically we could just change this to a get request and um you know save us this whole passing of the key and then then change the create API key to a get request I think that would make a lot of sense even though then that would be vulnerable to a cross-site request forgery because the if you don't know the um the authentication cookie if we take a look at this application under cookies and then localhost the authentication cookie the next auth session token this is what brings us the server side session right so we can check if the user is logged in with this cookie right here if you take it a look at the same site property it's set to lags which means that on get requests this is also sent a long even if they get request originates from a third party whereas with post requests this is not passed along this is like a bit more um Advanced of web security essentially if we turned the revoke API key to a get request which we definitely could because we don't expect any data in it this cookie would be passed along even in a request forgery from a third-party domain um I think that's not a huge security risk in our application specifically um well just keep that in mind if you decide to change this to a get request you might as well leave it a post request without passing any data that would work as well and I'm gonna figure something out for the final GitHub repository maybe just leave it like this it's not too bad in the first place I just want to mention that not to confuse you though and now let's get started in the similarity route to handle the actual Logic for our API Rod so let's go into the V1 folder that we already created previously let's close this down and close that down and in the V1 folder under Pages slash API V1 we are going to create a new file called similarity.ts this is going to be our main endpoint and from here you know the drill we're going to ex actually now we're going to declare a const Handler it's going to be equal to an async error function and at the very bottom we're going to export this Handler as a default so export default Handler and again we're going to wrap this Handler in or with methods API middleware oops oops I activate caps lock with methods import that and the method that we want to allow for this route specifically is a post request and then also pass the Handler along so we can only make a post request to this route now we're gonna do some interesting schema validation with Zod if you've never done that don't worry I'm going to explain every step of the way and it's not complicated at all so first off let's import Z from Zod and what this allows us to do is Define a schema of the request that we accept right so we can say the const rack schema is going to be equal to Z dot object now if you don't know what a z dot object is essentially we are creating a schema to then check the input that comes in so because we can never trust client input we don't know what sort of input we are going to get for this post route right somebody could pass anything as a post request to this API endpoint and now we want to make sure that the data that we get is specifically what we Define right now inside of this schema that we are creating and that the little typo so we can say that we are expecting the text one property to be there which is going to be a z dot string with a DOT max value of 1 000 characters so it can't be longer than that and then we also expect a text to property with a z dot string that is also going to be a max length of 1000 characters so if somebody passes like a text one a text 2 and a text 3 for example to this API Rod which they very well could if we take a look at Postman for example I've already prepared this API endpoint if you don't know Postman it's just a tool you can download to your desktop to simulate API requests it's super handy and inside of Postman we can take a look at the Local Host Road and we could pass along something like um in the body of the request text one text two and Nothing is Stopping Us from also passing text 3 right but if we pass tax three we won't need that in or API route so the schema is going to get parsened everything else that is not text one or text 2 is going to be removed completely we don't need those values and then oops and then if text 1 or text 2 is not present then this API out is going to throw an error because those are the values we need and you're gonna see what that looks like and when we do the API testing okay so let's have the Collins Handler and then first off we want to destructure the or declare the body as the request that we still need to get as a argument in this function or parameter I keep messing that up as a next API request and then the response is the next API whereas no not request as well next oops next AP Iris pawns and also import those types from next now we can say that the body is equal to the rec.body by default this would be any but we want this as unknown forcing us to properly oops unknown forcing us to properly validate the body with typescript now let's get the authorization header because when performing a request to this API endpoint users always have to pass along the API key that they generate within our app and to check that API key we can say cons API key that is passed to this road is equal to direct dot headers dot authorization super convenient that we get this from next shares and then we can say if there is no API key then we're going to return a res dot status of 401 unauthorized dot Json and say error unauthorized oops unauthorized great okay and now we can actually validate what comes in as the body of the post request with the schema we have defined above so we can say const parsed because these are the parsed values of the body it's going to be wreck schema dot safe powers and then pass in the body so the save parse does not throw an error if the parse fails so if the text 1 or text 2 is not present in the request body then the save parse is gonna not throw an error but instead the parse oops parse Dot success value is going to be false allowing us to handle this error a bit better in my opinion at least because we can say if the um if not post dot success so if this did not pass correctly if one of these values is not present then we can return a res dot status of 400.json telling users that they did a bad request and we can capitalize that okay so now after this guard close right here we know that there is a API key we don't know if it's valid yet though and we know that the powers was correct so we have 8x1 and the attacks 2 property in the request body let's enter a try catch block now and first thing we're going to do is destructure the text 1 and text 2 from the past so we can say const data is equal to parse and then we can destructure from the data the text one and also the text too and actually um to be honest with you I'm a bit unhappy with this implementation um so I think um what we're gonna do is delete this part the parsed and with the save parse and the parts success we're gonna delete that and then instead we're going to say the text one and the text 2. are going to be equal to rec schema dot parse body so this is going to actually throw an error that we need to catch in the catch block so let's do that right away and this is going to throw an arrow of type zot error that you've seen previously and we can catch that by saying if the error is an instance of Z dot zot error which it will be when this right here arrows then we can return a res dot status of 400.json telling the users that the error is an error.issues which we know exists because we did the type checking of the result error and let's just finish up the handling for now it's very fast so let's also return a res dot status of 500. dot Json telling the users that there was just an internal server error if we didn't catch this as a zot error as something must have went wrong somewhere and we don't really know what because the arrow is unknown great and now in the try catch block we can assume that the text 1 and text to exists and work with that so we can now check if the API key is valid we have so far only checked if there is an API key present but we also need to make sure it's valid right and we can do that by saying cons valid API key is equal to awaitdb dot find first oh and we still need to say dot API key DOT fine first and we want to find the first API key where and inside of the object brackets where the key is the API key that was provided and where the enabled property is true because that is a condition for an API key to be valid and if we have no valid API key if not valid API key then in that case we can return a res dot status of 401 unauthorized dot Json telling users in the error message so error colon unauthorized like that and now we can assume that we have a text 1 and text to property everything that we need to work with and the user has provided a valid API key so everything is fine and we can actually handle their request and now to measure the duration of the request that we're gonna do we're going to initialize a start it's going to be equal to a new date so we know how long the request has been running for how long it took so that we can display that in the dashboard to the user and then let's create the actual embedding so let's say const m bedding if you don't know what an embedding is we're using open AI to turn text into a long array of vectors like a long array of numbers essentially a vector and then we can compare the mathematical position of two vectors in the room and the closer they are together the more similar the text is now this is not some super complicated math algorithm that we're going to implement it's actually pretty simple to create this embedding we're going to say cons embedding is equal to and then await promise dot all because we're going to make multiple requests with promise.all we can make them simultaneously at the same time so we don't need to for one request to finish before we can make the other because one request does not depend on the other which is super handy so inside of this promise.all we can restructure the text 1 and text two and it's not really any structuring it's more like we are creating an array from these two and then saying dot map and for each of these so for text 1 and text two we are receiving a text and want to run a function for each text now because we are making an asynchronous request we also want to write this as async in front of the text and then in here we can handle the async logic that is now going to be executed simultaneously and the logic that we want is corn stress is going to be equal to a weight and now we're going to use open AI to create the actual Vector for us so it's going to turn the text that we have into a vector and the way we do that is by saying open AI dot oops and we need to import that open AI and we have not done that so we still need to do that in the lip really quick it's a very simple thing to do let's go into the source and then lib and then let's create a file called open AI dot TS and then let's say const configuration configuration is going to be equal to um new configuration that we get from open AI that is the npm package we've installed at the very beginning of this course and and here we're gonna pass the API key property which is going to be process.env and then whatever you call it in the env.example that you downloaded from the GitHub this is called open AI underscore API key and that is the only thing with that we need to pass into it and then we can say export const open AI it's going to be equal to new open AI API which is a class that we also need to import from open AI like that invoke that and give it a configuration to the Constructor there we go now to get this open AI API key let's go to the open AI website really quick openai.com and let's log in and they change the UI apparently how can we log in our product overview no all right I guess not let's go back and search open AI login and does that work open Ai No still doesn't work okay so they made it super confusing apparently let's go to the open AI API page available under platform.openai.com and now we can log in using Google and I'm gonna log in really quick you could use your Google account okay so I've opened this up on my other account and then when you have logged in I probably need to gonna I'm probably gonna need to censor some of these values we can go to view API Keys then here you can see all the API keys that you have and this is where you get the IP API key you can just copy it from here then close out of this window we don't need this anymore you can close that for now go back into our project in the EnV files and then in here we can paste the open AI API key actually you know what I think it's easier for you um if I just show you these values and then change them after the video so I'm just going to paste in my open AI API key right here save this because we also still need to do the redis URL as the last thing in the cnv file paste in your open AI API key we can close out of the EnV and remember to restart your server because we um redefined oops yarn Dev we defined and environment variable and now we can actually use the open AI value right here in our similarity route so we can import open Ai and then we're going to say dot create embedding and this create embedding creates a vector from a text that is super useful for us and for the model we're going to pass the text Dash embedding embedding Dash ada-002 which is just one model that does this really well and cheaply and then for the input we're gonna pass the text that we're mapping over and that's it now we can just return the res dot data dot data at the index of zero um the index of zero dot embedding which is going to contain the number array the like 1500 um length array of numbers that is representing the text as a mathematical vector and then the similarity of these two texts are going to be in or at least the vectors are going to be in the embeddings so this is a number array array and to get the similarity of these two under the promise.org we can say cons similarity is going to be equal to and to determine the similarity between two vectors we're going to use the cosine um product so the cosine similarity let's call that cosine similarity and and here we're going to pass the embeddings at the index of one and also the embedding oops embeddings at the index of um so the first one is embeddings at the index of zero and second one is embeddings at the index of one now this cosine similarity does not exist yet and we're going to create that in our helpers let's go to Let's close all of this source and then helpers and here let's create a new file called cosine Dash similarity.ts and this is just in algorithm to determine the cosine similarity let's export a const COS oops cosine similarity this receives two values the um the value a let's call it which is going to be a number array and then the value B which is also going to be a number array and inside of this function uh Y is okay export function cosine similarity there we go and by the way I'm I don't fully understand this either I just copy pasted this from stack Overflow um I don't know how to calculate the similarity between two vectors so I'm just gonna copy paste this from the GitHub I think that's the easiest thing let's go into the github.com and then similarity API let's go back into here and then just copy and paste this kind of I guess you could call it algorithm or whatever there's no need to write this out ourselves I don't fully understand it either but essentially it Returns the mathematical distance between two vectors and that's all we need to worry about because as a programmer as I mentioned earlier in the video you don't need to understand everything you just need to know how to Google well and then use code that is already there to your advantage you don't need to write everything from from scratch you really don't um so we're just going to paste this in there we know this works really well and now we can call the um the cosine similarity import this and the embedding let's call this embeddings there we go now we can pass the two embeddings as number arrays into the cosine similarity and that will return a value from 0 to 1 1 meaning the text is identical and zero meaning the text has nothing in common at all which rarely happens I don't know what you need to pass for that to happen and now that we've got the similarity of these two we can also calculate the duration because remember we initialize the start now the calculation is done and we need to know how long this took to determine how long this took we can say const duration let's call this is going to be equal to a new date let's invoke that and then dot get time and then from that we're going to subtract the start dot get time this is going to return to us in milliseconds how long this operation took and last thing we want to do is persist this right we've got the API request model in the database we want to know every API request that happened so let's say persist persist request and to persist this we're going to say oh wait DB dot API request dot create and what API request do we want to create we want to pass the data into here and let's take a look at what it will look like so first off we want to pass the duration we've already calculated that we don't need to worry about that further then we want to pass the method of Rec dot method as string then we want to pass the path which is going to be the request.urls oops as string and the reason we're saying s string right here is because direct.url M could be string or undefined but we know since this route is being run and we're and we are at this point there will always be determined so we can assume that they exist as string then the status is going to be 200 because if we're at this point it has been successful the API key ID is going to be the valid API key dot ID and then for the used API key that was used for this request and we also need to put a comma here for the used API key we can say the valid API key dot um oops dot key that's the API key we use for this request and then finally let's return a successful status return res dot status of 200 dot Json and as a response we're going to return the success of true then the text one that the user originally passed in the text to that the user originally passed in then the similarity as well okay so we've got that done and I think our API should be working at this point let's try to make a request to this API endpoint without the authorization header so in Postman let's just take this out but you could also mock this API request with a fetch in JavaScript if you don't have Postman or you could download Postman it's totally free and let's try out making a request at this API endpoint the URL we're going to enter is HTTP localhost 3000 slash API slash V1 similarity we are not going to pass an authority authorization header right now but we are going to pass text one text two and not text three for now let's send this request and see what happens and the response that came back I'm not sure how to zoom out can we just do this no that doesn't work okay so I need to zoom out a bit the error that came back is unauthorized because we did not pass an API key if we write some bogus value into the API key and send that the arrow is still unauthorized because this is not a valid API key so let's go into our application and generate ourselves an API key so let's sign out let's do the whole thing from scratch we land on the main page and then yeah that it needs to load a bit this page could not be found wait what the dashboard could not be fun um well anyways we still need to validate um all right the the page was not found because if there is no user on the dashboard which would never happen if the route was protected then we threw the not fund error just in case and we haven't protected the routes just yet and we haven't implemented the right limitation either but that's going to be pretty quick in the middleware first off let's try generating ourselves an API key and making a request with that API key so let's click request key we've got an A key we can copy this value put it into our authorization header and now let's try making that same request send that and it said internal server error that's not good let me see why that happened okay I'm pretty sure I found the error we made a little typo right here it's not text embedding ada002 but only with 2DS and now let's try making that request again and now it works just fine okay great so we've got this success true Apex one and text two and a similarity between these two texts of 0.941488 and so on really really good and now let's try revoking our API Key by the way we can see the request that we've done in the table right here and how long they took and then let's revoke this key and see what happens let's try making a request with the same API key and that should come back as unauthorized and it does because we've evoked the key let's try generating a new one and make a new request with this key let's paste it into the authorization how to check everything works correctly and that works just fine and let's try the last option of generating a new one and also um revoking it at the same time so create new key that's going to create a new key we can copy and paste it first let's check if the old one is invalid and it is and then now let's paste in the new one and see if this works it should and it does great so we've done the API Rod correctly and now there's one last thing that we want to do for this whole application we're almost done and that is protect the routes as you can see right now they're already kind of protected but it just says this page could not be found and that's not really what we want if the user is not logged in as we currently are like we're not logged in then we should be redirected to the login page instead of seeing this ugly this page could not be found we don't want that to happen and to do that we are going to create middleware across our whole application that's going to handle that logic for us and the middleware we always create at the same level as or Pages or app directory so let's create a new file in the source directory at the same level of these called middleware dot TS inside of the middleware we're going to handle all that logic so first off let's export default and from next auth we get something super convenient that is called with auth to use with this middleware now the Auto Imports once again don't work but we're going to import this from the with auth as a named import from next Dash auth and then slash middleware that's where we can import this from and we can invoke this with auth and inside of the with auth we can Define our async oops async function middleware that we want to handle custom logic in that middleware is going to get a request that is already typed out for us and I think vs code is doing it again Jesus Christ so let's let it load let's give it a hot second here for vs code to finish loading and then I'm going to be right back when I did there we go okay now I didn't even need to cut there it just worked great okay now the request is already typed this next request with auth because we invoke this within the next uh the with auth function and in here we can handle all the logic that we need for our middleware first off let's get the relative path that the user is requesting so the const path name is going to be equal to rec.nexturl Dot pathnamp and that's going to be the relative path of the URL so you know slash API slash V1 similarity or slash dashboard that's going to be the path name and then we want to manage rates limiting first so we're going to rate limit or API they can only make so many requests per interval and to do that and we only want this function to run on the API routes right not on the on the on the pages and to do that we can say if pathname dot starts with and then and here we can say slash API so this is only going to run on API routes and if it is then we want to get the IP of the one of the person that is requesting so we can say cons IP is equal to rec.ip or if this is undefined then it's probably localhost so one two seven point zero point zero point one as our localhost address and now we want to jump into a trike hatch block to handle the rate limiting inside of the try we can say that the const success we can destructure that it's going to be equal to and now a package from upstash is going to gonna come in super handy that is specifically made for rate limiting serverless functions we can say await rate limit import that from up stash slash red limit as you know that's not what we're gonna do um just await rate limit dot limit IP oops dot limit i p and now that is not going to be uppercase because this is something we're going to create ourselves right at the top here um so to handle the rate limiting function first off we want the redis right it's a in-memory database that is super fast so that's why it's very convenient for rate limiting we can say cons redis is equal to a new redis we can import that from add up slash redis we've already installed all the packages so we don't need to install that right now which is good and this takes the URL which is going to be equal to process.env dot redis underscore URL so this is a kind of like a database URL we've got for SQL just for the redis it's sensitive information because as with the database URL it also contains our database password so we want to store this securely on the server side and in an environment variable so it doesn't get exposed to the client side and then a token property that is process.env dot redis underscore secret that's what I called it in the example EnV file now to get this we're going to visit upstash up stash and upstash has two Services which are not two but the two that we are going to worry about are redis and then also the red limiting so let me quickly log in I'm going to do this in my other browser let me log in to upstairs right here on my left hand side and then you're going to be able to see what the console looks like that we're going to be using to manage our rate limiting it's it's really easy upset makes it super simple so we can go into a new database that you can easily create when you're new to upstash and then in here it's going to list or up stash read this URL and the upstate redis rest token which is the token you can just click this copy icon right here for the upstage redis rest URL when you're under um your project and then below the connector database this is for the year rest API and we can copy that value go back into our EnV file and then paste that value as the redis URL and then similarly um oh I need to go to the other browser similarly for the up stash redis token we can also just copy this value right here and then paste it into the redis secret and I'm not sure why it's blue so let's put this in quotation marks save that file and we should be good to go for redis that's all we need to do it's that simple it'll take less than five minutes to set up and for the red limiting we can say Collins rate limit this is going to be super simple we can just in this initialize a new rate limit class that we get from up stash slash red limit and this takes a redis which we've already created then also a limiter which is going to be the rate limit dot sliding window which is kind of like a Technique we can use for red limiting and here you we can see it's how many requests are all per window as the first argument and then combined approach of sliding logs and fixed window with lower storage cost and sliding logs and improved boundary Behavior I don't really know but it's good performance and that allows it to scale to high loads which is good and just for testing purposes we're gonna pass Five per one hour so we can only make five requests per hour and then right limit the IP that is making the request now if the red limiting is not successful if not success then we are gonna return a next oops next response that we get from next slash server dot Json and then here we can say oops error and what should the arrow be when people are making too many requests to our API endpoint we can just say too many requests and then if people are making more than five requests per hour they're gonna get this error for every other request that is above their limit and if there is Success then we're going to return null or next response dot next to pass on the action everything is fine and they are allowed to make a request or API route and if we're inside of the error we can just return the next response.json the same one is up here but we're just going to change the error message slightly we're going to say internal server error and that's the red limiting done it's honestly as easy as that it's super convenient with upstash and now we can manage the now we can worry about protecting or actual routes and to do that we know that the user has a JWT right and when they're authenticated the Json web tokens can be valid and if they're not allowed to access the route then the JWT is invalid so first step would be to get token to then check if it's valid or not and we can get the token um like that cons token by awaiting the get token method we get from next Dash auth JWT and in here we're going to pass the request so they automatically get the jwtu next session token from the request we don't need to pass it in manually which is super handy and then cons is off is going to be equal to we're going to turn the token into a Boolean so this is either a JWT or it's null if they're not authenticated and um so only when they're authenticated this is auth is going to be true and then next up we also want to determine one thing which is the if we're on an auth page because if you're authenticated already then you shouldn't be able to access the login or the sign up route or the register route in our case we only have a login route but that's fine um so we want to prevent logged in users from going to the login route we can do that by saying const is auth Page is going to be equal to rec.nexturl dot path name or we can just choose the path name we have defined up here pathname dot starts with and in here we can say slash login so only then is this in auth page that should not be accessed and then we can Define our sensitive routes const sensitive routes is going to be equal to an array we could pass in all sensitive routes in our case that's just going to be slash dashboard and then if we are trying to access an auth page and we are already authenticated if it's off then that shouldn't happen right in that case we're going to redirect them to the dashboard and we can do this by saying return next response dot redirect to a new URL that we're going to construct as a class and that goes to slash dashboard with the rec.url as the base URL so we're passing them onto the dashboard if they're trying to go to the login page when they're already authenticated and otherwise we can just pass on the request so we can return null next response.next um to just say yep you're fine you're free to go you're not authenticated and you're trying to access login that is perfectly fine and you are able to do that and then if the user is not authenticated and trying to access a sensor defraud and by the way we can try um we can check if they are trying to access this sensor defroad by saying sensitive routes dot sum enter for any route this returns true the following statement the pathname that starts with route if for any of the sensitive routes that's true they are trying to access the road and they are not authenticated so they shouldn't be able to do this action we can say um return nextresponse dot redirect and we want to redirect this user to again a new URL that we're going to construct and then we want to forward them just like up here to the slash login so they're not authenticated trying to access the dashboard we're just going to redirect them to the login and then pass the rack.url as a base URL and that needs to go inside of right here and then there's one final thing that we'd want to do and that is pass one options object to the with auth that contains the callbacks and for the callbacks we want to define the the async oops async as soon which is async authorized function and for this we always want to return true and this um I wrote this into the GitHub repository this is a workaround for handling redirect on auth pages we return through here so that the middleware function above is always called that's the reason we need to include this and um yeah now we are almost done with the middleware finally we want to export a const oops export cons config to determine when this middleware should run because currently it doesn't know that so we can pass an object in here that takes an array for the matcher and for each um string that we pass in here for that route this API will run so we can pass the slash for the home page we can pass the slash login we can pass slash Regis well we don't even have a register road we can pass the slash dashboard slash then any path after that so we can do colon path star to match any path so it doesn't matter what comes after the dashboard and then we could also match the um slash API slash and then any path that follows the API and now this middleware is going to run when any of these strings in this array is in the URL bar and determine whether the user is allowed to perform this action or whether they are not allowed to do that okay I saved the middleware let's close out of this and now we already know that the API is working let's try to access a route that is protected while not being logged in so we can close out of upstash for now and let's go into an incognito tab let's try to access the dashboard we shouldn't be able to do this remember we're not logged in and now we are being redirected to the slash login the slash login doesn't exist yet and that's the page we completely forgot but it's a very simple page we can just fix that really quick let's create the login route that should definitely exist let's go into or a source and then app and then let's create an auth in here and in here we're going to create a new folder for the login in which we can create the page.tsx that we need for the next.js route to be working okay now the login page is not going to receive any props we can get rid of all of this stuff remove that and then build the page out together so the first div that we're going to have on the login page is going to get a class name of absolute in set zero if you don't know in set 0 it's just top right bottom left alt 0 pixels in regular CSS in MX of also to Center this a container property we want this to be Flex have a height of 100 VH so height screen Flex Dash call items Dash Center justify Dash Center and I will add a little typo there justify Center and that's it then let's format that using prettier and have another div in here that has a class name of mx-auto flex with a full Flex Dash call justify Dash Center space Dash y-6 if you're wondering what that is in regular CSS it's kind of a little workaround between margin top and margin bottom it's kind of like the same as Gap we could also say you know Gap six it doesn't matter and then we can say Max with of large and then let's have one last div in here to make sure the sliding is just right with a class name of flex Flex Dash column items Dash Center Gap dash six and then text Dash Center and then inside of this div is not going to be another div but a link component that we get from next link with a class name of the button variance so we're going to make this look like a button button variance import those button variants that's what they're super useful for just making any component look like a button even if it's not just like the link the variant is going to be of ghost and a class name we're gonna pass a custom one class name is gonna be equal to W fit so the button is not going to take up more space than it needs to and this link is going to have an href of the home page so just a slash to navigated the homepage within the link in here we're going to render out the icons dot Chevron left to indicate that we go back to the home page with a class name of margin rate of 2 height of 4 and also a width of 4 and that's going to be a self-closing icon and the button is going to say back to home great let's take a look at what that looks like we can close this tab and go back to our login route reload this and take a look at what happened we can see the back to home button it works but we're not done yet with the login page and let's turn this into a split screen again so it's a bit easier to see what we're doing and then after the button we're going to have a large heading our shedding which is going to say welcome back and then below that a paragraph saying please sign in using your Google account great we've got that done and then one final thing that we need to implement in here is the the actual Google button right if we navigate to the login or we could press on the API key that's also going to take us to login we can see back to home welcome back please sign in using your Google account however there is no button yet to perform that action so let's create that right now and let's go to components and then new file let's call this component a user auth form dot TSX oh and we already oh wait that's the that's the original project hold up we want to go into source and then app and in here no and then in the components and then here we're going to create the user auth form dot TSX there we go okay and inside of this user auth form um let's initialize this as a functional component and the user auth form we can extend this to take more props however we can also just not do that make it a bit simpler and not take this and not let this take any props okay now for this user auth form the functionality is going to be um very simple we're just going to have one button in here with an unclick Handler which means we need to use the client directive use client and then let's render a div inside of here with a class name of um not a regular string but we're going to use our CN utility function to merge some classes together instead of the CM we're going to have a name of flex and then justify their Center so the content is in the center and that's it and inside of the div we're going to have a button or a custom button um and the button is going to get an is loading of some state that we need to keep in this component so essentially while the Google login is loading we want to show a loading State inside of this button and we can do that by saying or by using state so let's call that state it's loading and set is loading which is going to be equal to use state that we import from react that is going to be false by default and we could we could if we wanted to split explicitly type this out as a Boolean value so if we change it later we get errors but we could not do that it really doesn't matter I just think it's a bit easier to immediately see what it's about then that's create or sign and function really quick it's very simple let's call it um login with Google it's going to be an async error function and first off we want to set the is loading to true because we want to inform the user yep we're currently loading let them know um that it is loading which makes for a better user experience after that we're going to initialize a trike hatch block and try to await the sign in that you get from next dash off slash react and we're going to pass this a string of Google to sign in with Google and next up in the catch block if we get an error we want to alert the user with a toast notification to do that we're going to import the tools from our custom UI pass it a title of error let's capitalize that pass it a message of there was an error logging in and then finally the last property is going to be the type property and this toast is of going to be type error and then finally no matter whether we Arrow or not we want to set the is loading to false to inform the user we're done loading and so the is loading for the button is going to use the same state the is loading that we have defined above okay um great now the button is going to get a class name of Max oops Max W of small and with a full and then finally a background Dash slate Dash 200. or actually not let's not do the Slate one I think it will look better without it and so what we're saying here is it should always fill up the maximum width which is going to be small and on click we still need to pass that on click it's gonna be the function we've defined up here the login with Google that is what's going to happen on click for this button and then we want to pass a disabled and it's going to be disabled when this is loading and I think the button does that automatically actually let's check that button.tsx it should automatically be disabled yeah we don't even need to pass that explicitly we can remove this it's already going to be disabled when it's loading anyways great and one final thing that we want to display is the Google icon inside of the button what we only want to display that when the button is not loading and else in the same spot of the Google icon you want to display the loading spinner and the way we can do that is by saying if is loading then we're gonna return null and else we're going to return the Google SVG now the SVG we're not going to write ourselves uh that would make any sense let's go into the GitHub repository oops um enter ticket type repository and then just copy the Google SVG from there so visit the repo go on to the source directory enter components and then it should be add user auth form scroll down a bit until you see this SVG right here and we can just copy that SVG go back into our code editor and paste the SVG right here inside of the parentheses paste the Google SVG and then below the SVG we can render the text of Google great we are exporting that as default and now when we go back into the browser and take a look at the login page we can click there or we could type it into the dashboard well okay we're already logged in so we can't access the login page which makes sense because we've protected that route let's refresh the login and apparently it doesn't work yet probably because we've not imported the user all form on the page and yeah we indeed haven't so that's imported here as user or form on the page right below the live chatting and the paragraph let's save that and take a look at this route again let's read out the page and it should hopefully show the Google button and yes it does what happens when we click this button there was a loading indicator and then it's logging me in I'm already signed in with Google so I don't need to do that again but in your case the Google login would pop up if you're not already logged in really really good stuff I think we're done with a project the API keys are working the login is working the light and dark mode is working we've got a beautiful home page with a nice illustration um right here we've got a dashboard listing or previous API requests with different API Keys even if they have been revoked we've done the database integration we are pretty much done if you're following along to this step honestly congratulations um you've done really good work so far really really good and the last step would be to publish this to the website so you can show it off to potential employees and friends right and the way we do that is by using GitHub and deploying to versl because that's the easiest thing you could also deploy this to AWS um I'm probably going to make a video on that soon but um that is probably only suitable for large projects let's go to new and then a private Repository or you could make this public if you wanted to we can call this a similarity YouTube Dash YouTube and I'm assuming that you already have a GitHub account right here um You probably do let's create a private Repository create Repository and then let's get to working on pushing this up to vers cell so let's go into or we can close the original file we are done before we do that though let's run a final lint to see if we did any obvious errors and if the build would fail that we need to fix and that is not the case we can run the yarn build command just to make sure that this project builds correctly before um trying to deploy it over cell however let's go back into the GitHub and then copy this remote origin that we need to deploy this project later let's wait for this build to finish and hopefully it builds um just fine because that means that the production deployment is also not gonna error which would be great and it doesn't look like it has any error now this is totally fine collecting page data let's give it a hot second and see if it loads correctly and it did great um the build loaded correctly so that means we can now get at dot if this is a private website you don't really need to worry about git commit messages in the first place so we can say git commits Dash M and then initial commit for example it doesn't really matter what you put in there as long as you're not working for other people or with other people then it would actually make sense to have something like a a certain rules for what you can write in those commit messages um but for now it doesn't matter and then we want to paste the remote origin of our GitHub repository that we got from the the GitHub page right here I can close that and just paste that remote origin in here press enter now we've added the GitHub repository to this project and we can now say git push M Dash U origin main so that stands for Upstream so we're pushing this Upstream to GitHub we can press enter and that worked we successfully published all the changes into our GitHub repository um and we can also see that if we go back to the repo we can see all the files that are in here great and now we can go to the ver cell dot com to actually get this deployed let me log in using GitHub and hopefully I don't have to enter my name again correct now we can click here on the top right side and go into project and in here we can import the similarity Dash YouTube project that we have just created inside of GitHub we can click import Define all the environment variables so this is where you just copy the dot EnV variables from your file like the next auth secret the Google client ID the database URL you just copy the key go back into your deployment paste it right here and then do the same thing for the value and just copy and paste that as well click add and do that for all the environment variables except for this one we don't need to do it for that same for the Google client ID and so on I'm probably going to fast forward this um because this is a pretty simple task and then I'm gonna see you when I'm done passing all of these okay I added the last environment variable and now let's click deploy and see what happens it will now go through the whole build process and log it out right here in the building tab the one we've previously done and because we've already built the project we know this is not going to fail um you know it just does the same thing on the Versa servers that we did locally which will work and then we can see our project in the web actually show it to friends and employees and one more thing we're gonna do is test if the red limitation is actually going to work in this project so let's give this a hot second to load and deploy and then I'm going to be right back when it has successfully deployed okay so it looks like it's almost done it's creating an optimized production build so let's give this a minute and see what happens compiled now it's doing the final lint seems to have worked no errors so far it's generated the pages and I think now it is done great so it took about 40 seconds for this deployment and then in the second here you should be able to see a message telling us that um it has deployed an actual domain in the web that we can then make our requests to and this is it let's give this a second to load the preview or we could already yes very very nice now we can continue to dashboard and copy or just visit this URL see if the deployment has worked and it has we can see the whole page light and dark mode are working really nice and now let's copy this URL and make a request to that in Postman so let's paste the URL in here make a post request to slash API V1 similarity and let's just not include an authorization header see if that works it doesn't work it we get an unauthorized okay so let's create our key let's sign into the actual web application okay and we also need to include this URL in Google by the way that's why we get the access blocked the request is unacceptable or something long of those lines because inside of the Google console um Google oops Google Cloud console in our project we used for this app the YouTube test in my case let's go to sign in data and then to well you would have your oauth 2.0 client at least I did this for the actual production project and you would also need to add this domain right here and the domain that yours um if you have a custom domain that as well so Google knows this is a valid URL to login from I'm not going to bother because I already did that for the main main app but just add the URL right below the localhost in both places for the authorized JavaScript and then the Uris that are allowed and then you're totally golden um it's very very simple and then you can log into your application we already already know an API key and let's just copy this API key it should also work in the um in the actual web application because the database is in fact the same and so let's add an authorization header to our request paste the API key in here click Send and now we get an error of invalid type expected object received string and probably because we have not passed anything in the body such as text 1 and text 2. so let's say hello as text one and the world as text to and send the request again to our actual web deployed endpoint and it works just fine we get back the value really really nice so now we know that this endpoint Works let's try spamming it and see if the red limitation works and it does we get the error of too many requests beautiful beautiful and that sums up our work on this app the red limitation works just fine the dashboard as well and if you add the URL to the Google Cloud console then the login and everything else is also going to work great work honestly if you followed along throughout this video really really good work I'm sure you got a lot of value from it I put a lot of effort into making this project and the video and going through the deployment and everything else together with you and I really hope you enjoyed I'm gonna see you in the next video which is probably going to be a bit shorter I'm really not sure how long this is going to be but um definitely more than five hours I believe so thank you for watching if you followed along um I really hope you enjoyed the video and got a lot of value from it and are now a way better next year s13 developer that's it from me thank you for watching again I'm gonna see the next one have a good one and bye bye
Info
Channel: Josh tried coding
Views: 142,276
Rating: undefined out of 5
Keywords: build your own api, full stack, josh tried coding, joshtriedcoding, modern, next auth, next js full course, nextjs, nextjs 13, nextjs auth, nextjs authentication, nextjs beginner, nextjs project, nextjs tutorial, radix ui, rate limit, react, react beginner, tailwind, tailwindcss, tutorial, typescript
Id: 4lUkSgvmTYM
Channel Id: undefined
Length: 324min 1sec (19441 seconds)
Published: Thu Mar 09 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.