Create A Booking App From Scratch | React, Next.js, Appwrite, Tailwind

Traversy Media| 04:12:42|Mar 26, 2026
Chapters43
The video outlines building a booking app for meeting rooms using Next.js and Appwrite, covering auth, file storage, rooms management, and protection against double bookings with a long, multi-step project sponsored by Appwrite.

Traversy Media builds a full booking app from scratch using Next.js 13 App Router, Appwrite, Tailwind, and server actions—covering UI, auth, storage, and bookings with real-time-like flows.

Summary

Brad Traversy walks through building a complete meeting-room booking app from the ground up, integrating Next.js 13 App Router with Appwrite for auth, data storage, and file uploads. He starts with UI scaffolding (Tailwind-powered components, header, footer, room cards) and then progressively wires real data via Appwrite, replacing static JSON with server actions. The tutorial covers authentication (login, register, middleware protection), creating rooms, uploading images to Appwrite storage, and a fully functional bookings system with date-time handling, overlap prevention, and a booking view. Traversy also demonstrates a practical deployment flow to Vercel (and configuring environment variables) and shares tips on structuring code with server actions, actions folders, and a global authentication context. Throughout, he emphasizes not using API routes for data access, preferring server actions for a cleaner, monolithic app, and shows how to connect the front end to Appwrite’s SDK for a production-like setup. The result is a feature-rich CRUD app with rooms, bookings, image uploads, and authentication all in one coherent workflow.

Key Takeaways

  • Appwrite provides a free tier for building and testing the project, including databases, storage buckets, and authentication, which Brad uses to replace mock data with real data storage.
  • Server actions are demonstrated as the preferred Next.js pattern over API routes for server-side data operations, enabling a cleaner data flow and simpler data fetching on pages.
  • A complete data model is built: rooms (with images, address, availability, price), bookings (user, room, check-in/out), and a relationships-based setup that supports lookups (booking.room).
  • Image upload is integrated via Appwrite storage buckets (rooms bucket) and linked to room records, with a plan for dynamic image URLs served through Appwrite storage.
  • Middleware and a global authentication context are used together to protect routes and update the UI in real time when login/logout events occur.
  • Travel-ready deployment steps include pushing to GitHub and importing the project into Vercel, plus configuring environment variables and the public URL for the deployed app.

Who Is This For?

Essential viewing for React/Next.js developers who want to build a full-stack CRUD app with Appwrite—from authentication to file storage and bookings logic. Great for learners who want a production-like pattern using server actions, middlewares, and a real backend in one cohesive project.

Notable Quotes

""We’re going to build a booking app for meeting rooms... using Next.js along with AppWrite""
Brad introduces the project and the tech stack.
""There’s a very generous free tier so you could follow along. You don’t have to pay for anything.""
Brad highlights Appwrite’s free tier availability.
""So we’re going to add protection so that you can't double book a room for the same time frame.""
Key feature: preventing overlapping bookings.
""Server actions are demonstrated as the preferred Next.js pattern over API routes for server-side data operations""
Brad advocates server actions as the main data layer approach.
""Deployment flow to Vercel... configure environment variables and the public URL""
Brad shows how to deploy and connect environment variables.

Questions This Video Answers

  • how do you implement server actions to replace api routes in Next.js?
  • how does Appwrite handle file uploads and storage buckets in a Next.js app?
  • how can I prevent double-booking in a room booking app using date-time logic?
  • how to deploy a Next.js app with Appwrite on Vercel and configure environment variables?
  • what are best practices for building a full-stack app with Next.js, Appwrite, and Tailwind?
Next.js 13AppwriteTailwind CSSServer ActionsMiddlewareAuthenticationAppwrite StorageLuxonReact IconsReact Toastify
Full Transcript
Hey, what's going on guys? So, we're going to do one of the larger projects that I've done on this channel. It's a 4-hour long project, and we're going to build a booking app for uh for meeting rooms or conference rooms for companies. And we're going to be using Nex.js along with AppRight, which is uh just an all around platform for databases, for authentication, for uh file storage. We'll be storing our images for the the rooms that we that we're booking. Um, so there's going to be quite a bit that goes into this and we're going to be using Nex.js actions as well. So, AppPright is sponsoring this video. Um, there is a very generous free tier so you could follow along. You don't have to pay for anything. You don't have to put in any credit card information. You can create your databases, your storage buckets and all that. Um, so I would suggest following along and we'll be able to authenticate. We'll be able to add rooms to rent out. will be able to book rooms. We're going to add protection so that you can't double book a room for the same time frame. So, there's going to be quite a bit um that goes into this project. It's going to take a while. So, you could just do it, you know, in increments and it might take you a few days. It might take you a few weeks depending on your schedule. But, I think you're going to learn a lot from it when it comes to React, when it comes to Nex.js, when it comes to AppRight. So, that's it. Let's go ahead and jump into it. All right, guys. So, before we do anything, I want to give you a quick demo just so you know what you're building and what to look forward to. All right. So, and we're going to start just by creating some of the UI like the navbar, um the the rooms here. We're going to use some static data at first in a JSON file, and then we're going to create our app database, create our storage buckets, install the SDK, implement authentication, and all that. So on the homepage, we list all the available rooms. You can see there's an image. There's um just a name, address, availability, and price. Click view room. Shows us some more info. And we have the booking form. Now, if I try and book a room now, notice I'm I'm not logged in. So, it's just going to redirect me to the login. So, let's quickly register a new account. So, just put whatever in here. And password. And again, authenticate, all authentication, everything is through appreite. There's no uh no other libraries or anything like that. So, I'm going to log in. Bradgmail. All right. So, now I'm logged in. I have some some new options up here. So, my rooms, if I add any rooms to to rent out, those will be shown here. If I book any rooms, those will be shown here under bookings. So, let's quickly just add a room. I'm just going to use my form filler here and I'll change the name though to my room and we can upload an image and again this is all through through apprite the storage buckets. Go ahead and save that. So room created successfully and you can see my room right here. Click view room takes us and now it can be booked. All right. So that's how we can add rooms. Now let's say we want to book a room like this training room. And now that I'm logged in I should be able to do it. So, let's book it for tomorrow. Let's say we want it at um we'll just do 1210 12:10 p.m. to let's say 2:10 p.m. All right. So, I'll go ahead and book that. Takes me to my bookings page where it shows all your bookings and the check-in and checkout. You can cancel it from here as well. And we're also going to write the logic so that if it's already booked between a certain time, you're not going to be able to to book another, you know, make another booking in that time. So, for example, if I were to try this again and I did what 1210 to 210. So, let's say we want to do one 111 to whatever 311. So, that shouldn't work because it's within the time frame of the other booking. So it says right here, this room is already booked for the selected time. So if I go and I do what did we end it at? 3:10. So we can do like if we started at 3:11 and go to whatever 611. That should work. Okay. So now that gets booked because it doesn't interfere with this time range. And of course I can cancel the booking. So that's pretty much the gist of of what we'll be building. All right. So, yeah, we can sign out. So, let's get started. And also, I'm going to put the link to the repository in the description, and it has the full app as well as the theme files, which is all the HTML with all the Tailwind classes. So, we we will be grabbing a little bit of the the markup and classes just so we don't have to type out a million classes. All right, so let's get started. I'm going to open up my terminal. And you want to just go to wherever you want to create this. And of course you need Node.js installed because we're going to run npx. And then we want to do create d-next- app at latest. And then I'm going to call this book it. And then we'll go through and answer these questions. So TypeScript, I'm going to say no. Of course, if you want to use TypeScript, you can or ESLint. I'm going to say no to those and yes to Tailwind, no to the source directory, yes to the app router, and no to the customizing the the alias. So that's going to set us an application with Nex.js, ReactDOM, and React, as well as the dev dependencies of Tailwind and post CSS. So now I'm going to open up VS Code in that booket directory that I just created and I'm going to open my integrated terminal because I'll be using that from now on. And we'll just go ahead and run the server. So npm rundev that's going to run on localhost 3000. Just going to uh I guess I'll leave I can leave this open the the final version, but let's move this over here. All right. So now we have just the the landing page. the Next.js landing page. And I just want to let everyone know that this is not like a a crash course. You should know Next.js, at least the very basics, how to create a page or a component, uh how filebased routing works. I'm not going to explain that. If if you're brand new to Nex.js, then go ahead and watch my Nex.js crash course before you watch this. So, what we're going to do is open up the in the app folder. The page.js JS is the homepage right here. Um, so I'm going to just kind of clear this out. Get rid of that image import. And then everything within the the div I'm going to get rid of and get rid of all these classes. In fact, we don't even need a div. It'll just be a a fragment. And then I'll just put an H1 for now and just say book it app. Okay. And then for the styling, um, this global CSS, we're using Tailwind, so we want these directives, but everything under that we can get rid of. So just delete that. And if you want to keep the global CSS in the app folder, you can. But what I do is I like to create a folder outside in the root called assets. And then in assets have another folder called styles and then move the global CSS into styles. Now, if I do that, it's going to break because in the layout where the CSS is being brought in, it's not being brought in from the right place. So, I just want to change that now. Oops. I want to change that to at slash and then assets slashstyles globals.css. And that should fix it. All right. Now, there's a couple more things I want to do in this layout file. So they're now using this gist or jist I don't even know how to say this this font and I want to use the inter font which is what they used to use. So I'm going to just instead of importing local font I'm going to import enter uppercase I and that's going to be from next font google / google and then we can get rid of these two right here and create a new variable called enter uh yeah lowercase enter and set that equal to uppercase enter and we're just going to pass in here let's say subsets set that to an array with a string of Latin. Okay. And then I want to use that font globally or in the body here. So for the class name, we're going to set it to enter do.class name. And that should change the font to enter. All right. Now, um the metadata, I'm just going to change this. You can put whatever you want here, but I'm just going to say book it app. And I'll just say book book a room. And then for the description, uh, I'll just do book a meeting or conference room for your team. All right. And then the last thing I want to do here for now is the children. This is where, you know, all the page content is output. I just want to wrap that in a main tag with some classes. So right here, let's say main. And I'm just going to add uh a couple classes. So let's do MX auto. So margin auto on the X axis. Going to do a max width of 7. We'll do 7 XL. Padding on the Xaxis 4. Padding on the Y ais 6. And then I'm also going to add on small screens and up. So SM colon PX6. And then on large so lg colon going to do px8. All right. And then in the main tag is where we want to move the children prop. All right. So that'll just kind of give it a container. So now let's create our first UI component which is going to be the header. So in the root we're going to create a new folder called components. And then in components let's create a file called header.jsx. JSX. Now, I like to use the JSX extension. So, I'm actually going to change both of these as well. So, layout, we'll change that to JSX, as well as the page. Change that as well. And that should still work just fine. Okay. Just reload. That should Yep. So for the header, I'm using the VS Code extension react simple React snippets where I can just do sfc enter and it'll give me an arrow function with the export at the bottom which is like how I like to format my components. And then we'll just call it header. Oops. And you can just kind of tab into the props which this doesn't have any. And then tab into the return. And for now let's just put in we'll just say header and just get it on the page. Now, I want this on every page, so I'm going to put it in the layout. So, in layout, let's go ahead and import header. And then let's go down right above the main tag. And we're going to embed the header component. And now you should see the the header text. Now, as far as the what we want to put in the header, there's quite a bit of HTML and and Tailwind classes. So, what I would suggest doing is cloning or downloading the repository and then taking the theme files which are right here underscore theme files and just putting them in the root of your project. Just copy the folder. If you do that, then you'll have easy access to the HTML. All right. So, I'm going to open up the index html and what we want is the entire header tag and everything in it. So, I'm just going to grab this the mobile menu as well. So everything in the header and then we're going to just put here some parentheses and then we can paste that in. Now I have some HTML comments in here to kind of break things up. So you're going to have to just either delete them or if you want to keep them, which I'm going to then just use you just want to use a JSX comment by doing command or control forward slash to comment it out like that. I believe there's five. So that's one, two, and then this one. three, four, and five. I think that's all of them. Yeah. So, we save that. You should see it. Now, we obviously we want to change class to class name. So, if you highlight one and then just do uh command or control shift L, that'll select them all. And then we'll change all of them to class name. All right. Now, the way that this this menu works is on large screens, you have your, you know, your left and right side. We'll fix the logo in a minute, but on smaller screens, the left side is going to get knocked down like that. Okay. So, there's no hamburger button or anything. Um, and this does look cluttered because it shows every link, but in reality, when we're, you know, later on with authentication, login and register are only going to show when we're not logged in. My rooms and sign out are only going to show when we are logged in. These two will also be when we're logged in, so it's not going to look as cluttered later on. All right, so let's take care of um the logo next. Now, if you look in the theme files, there's an images folder and there's a logo SVG. So, what I'm going to do is in assets, the assets folder that we created, I'm going to create another folder in there called images. And then move or not move, but copy the logo from here into assets images. And then we what we can do is bring that in to the the header. And we're going to bring in a couple other things as well. So, we want the image component from next. And we also want the link component. So, we might as well bring those in now. and then bring in the logo itself. So let's say import logo and that's going to be from at slashassets slashim images slashlogo.svg. Okay. And then we'll go ahead where down here where we have the image tag replace that with image like that and change the source to the logo variable. So, source equals brackets logo. And we just also want to add here priority and set that to true. All right. So, if we do that now, you should see the logo. All right. Now, next thing, we brought this link in and that's going to replace all these a tags. So, there's there's quite a few here. You can go one by one if you want, but I'm going to just put a cursor in front of each one. I'm going to hold down option or alt on Windows. Click here. So, wherever I want to put the cursor, this one, this one, uh, this one. So, all the a tags right here. We get the mobile menu. So, here, here, and here, I think is the last one. And then I'm just going to delete backspace and then put in link. And that also replaced the ending tag. So, make sure that that's formatted correctly. All right. So, save that. Everything should still work fine. If you open up the console, we shouldn't see any weird errors or anything. All right. Now, the last thing I want to do for now in the in the header is the icons because these links up here, if we look in the final project, you'll see they have these icons. So, um, what I did in the theme files is I just used the CDN for font awesome. trying to find one of them and then just added these eye tags with the classes. But with React, I use uh a package called React icons. So, I'm going to open up a new uh new terminal. I'm going to leave the other one, leave the server running in that one. And let's say npm install react- icons. Okay. So now what we can do is bring in the icons as components. So let's say import and this is these are all going to be from react icons slashfa because it's the font awesome library because there's other libraries you can use as well and the icons I want are going to be fa user we want fa signin alt fa sign out alt and fa building. Those are the four icons we want. And then we're just going to replace the I tags. I'm just going to search for angle bracket I. And let's see. So right here, I'm going to change this or or just get rid of this whole I tag. That's the sign in. So that's going to be FA signin alt. And then I'm just going to add two classes. So class name. And I want it to be uh inline and margin right one just to add some space. So, if I save that now, we should see the little login icon. All right. And then what I'll do is copy that and go down to the next one, which is register. Replace this I tag. And this is going to be the user icon. So, FA user uppercase U. Oops. So, if we save that. There we go. And then we have the login one or sorry, log out. So that's going to be sign out alt and then the building one which is going to go oh right here. So my rooms replace that with fa building. So now you should have those four icons and that's all I want to do right now for uh you know for this for the header. Later on when we have authentication we'll be doing some other stuff but we can close that up for now. And then I'm also going to add a footer. So let's create a component called footer.jsx. And I'm going to do sfc footer. And I'm just going to grab from the index html. If we go down to the bottom, there's a very very simple footer. So I'll just grab this and we'll put that right in here. Okay. And then for the for the year though, I'm just going to I want that to be dynamic. So I'm going to say const current year and let's set that to new date and we'll use the get full year method and then just replace 2024 with brackets and current year so that way it changes on its own and then yeah we'll do that and then in the lay uh layout yeah layout jsx we're going to bring in footer so port footer. And we're going to put that right below the main tag. So And I mean, you can leave the background gray if you want, but I think I'm just going to get rid of that class and just have it be just white like that. All right, cool. So, we got the header and the footer. We can close the layout. Now, the next thing is the the rooms on the homepage. And like I said, I'm not going to dive right into using appite because I just want to style it first and then we can integrate appite and we don't have to worry about all the classes and stuff. So if you look in the repository, there's a data folder and there's a a rooms.json file. So what I'm going to do is bring that data folder into the root of my application. So data, bring that over. I'm just going to copy folder. Okay. And then we can what we can do is bring that in to the um to the homepage. So let's go up at the top here and let's say import and say rooms and that's going to be from let's say at slashdata slash and then rooms rooms.json. All right. Now I have access to those rooms. So, I'm going to get rid of this H1 and open up some curly braces and let's add uh I'm going to check to see if there are rooms. So, let's say if the room's length is greater than zero then parenthesis else parenthesis and in the first parenthesis is if there are rooms. So if there are, then I'm going to take those rooms and I'm going to use map, pass in our function here, and say for each room I want to show. For now, we'll just do an H3. Ultimately, it's going to be a card, but for now, we'll just do an H3. And just to make sure we're getting the data, let's say room.name. And then down here in the else, if there's no rooms, then we'll just put a paragraph and say no rooms uh available at the moment. All right. So, let's check that out. Yeah, right here. So, it's just showing us the names of the rooms in the JSON file. And just to take a quick look at that file, there's an ID or dollar sign ID because that's how it's formatted in AppRight. And then a user ID because we will have a relationship between a user and uh room collection. And then the name, description, square feet, capacity, location, which is just like where on the compound or in the building it is, and then the address is the full address, amenities, which is a commaepparated list, availability, price per hour, and image. And ultimately, the image URL will be it'll come from apprite, but for now, it's just going to be static images. All right, so we have that going on. Now let's create our room card component that we want to show for every room. Right? So for that I'm going to go into components create a new file called room d uh uh room card.jsx. And in here we want to create a component called room card. And then as a prop we want to pass in a dstructure room just room. And then as far as what I want to return, it's going to be some HTML. So again, I'm going to go back into the theme files index and go up to where we have the rooms. And I labeled them. So we have room one. So right under that comment, this div, which ends right here above room two. I'm going to grab that. So I tried to make these theme files really easy for you to just grab things from. So let's put that right in here. And then of course uh are there any comments? I don't think there's any comments, but we need to change the classes. So command or control shift L change that all those to class name. Okay. Now I'm just going to save it as is with the hard-coded data for now just to bring it in to our uh homepage. So on the homepage, let's import room card. And then instead of this H3 right here, we're going to um with room card and we're going to pass in a room prop and pass in room. All right. So, if I save that, now we're going to have basically just this static data for for every room that is in that JSON file. So, let's start to make this dynamic. So, back in room card, a couple things we want to bring in. So, we want to bring in image since there's an image. And then we want to bring in uh link because there is a link to the actual to the full page, the room page. And then let's um let's start with the image. So, instead of an image tag, we're going to use the image component. And the source for that um basically this the the room images ultimately like I said are going to be using the storage buckets in appreite but for now what we can do is take the images you can see in the theme files there's a folder called images and a folder called rooms inside of it. So let's create a public folder in the root. So, public and then in that we'll have a folder called images and then I'm going to move the rooms or copy rooms in the theme files images to the public images like that. Okay. Then, and even if I reload now, it should show just that room one because that's in there. But we want this to be dynamic. So let's change the source to uh back tick slashim images slash rooms and then we want the dynamic room name uh image name which is room image. Now with this image component we're it's going to make us add a width and a height. So I'm going to add a width of 400 and a height of 100. Save that. And now we have our images. Okay. So, next thing we're just going to kind of go through and add the dynamic data. So, we have the name here. Let's get rid of that. And let's do room.name. We should also change the alt here. So, we'll change that to room.name. And if we save it, you should see different names now for the rooms. Okay. Let's move along. Here we have the address. So, I'm going to replace that with room address. Then we have the availability. Let's get rid of that and add room availability. Then we get the price. So, I just want to replace the number here, the 150. I'm going to replace that with room dot and it's I believe it's price underscore perc_hour. Okay, let's save that. Check it out. Yep. So, that works. Okay. And then for the link here, first of all, this a tag is going to be a link. And then we want for the href, let's make this some back ticks. and it's going to be slashrooms slash and then the room ID or money sign ID. Okay. So, if I were to click on one of these view rooms, it goes to rooms two and it's fine. It's it's 404 is what we should be getting at this point. But the point is if you go to rooms slash and it should have that ID. All right. Now, there's a heading that I want to add. So if we look here, there's this available rooms and every page really has a heading like that. See the So let's make that a dynamic component. So I'm going to create a new component and we'll call this heading.jsx sfc heading. And then that's going to take in a title. All right. Okay. And then I'm going to grab from the index html right above that room one is this section. I'm going to take that and I'm going to put that right in here. And then just change this to that dynamic title. Okay. Now I'm going to bring it into the homepage. So I'm going to import heading and then we're going to put that right above the brackets, the curly braces. So, let's say heading and we want to pass in a title of available rooms. And there we go. So, that looks a little better. And I think the last thing that we'll do before we get into, you know, getting into appite and starting to integrate that is do the single page, right? So, this page here. Now to create a a page or a component for this URL, we have to go into our file structure, go into the app folder and create a folder called rooms. Okay? Because we're going to slash rooms. Uh and I we can get rid of this fonts folder. That's not necessary anymore. Uh but in that rooms, we want some we want to represent this ID. So we need to create a folder with brackets and then ID. And then inside that is where we create our page.jsx. And then let's do sfc. And I'm going to call this uh room. Let's call it room page. And for now just put you could just put whatever in here. I'm just going to have a fragment and just say room. And there we go. So no matter which one we go to, we're just going to see room. Now we need to get the ID which is in the the URL. So we can do that pretty easily with Nex.js. And this is a server component. Every component you create in Nex.js by default is is server side. Um if you want it to be a client component then you would put use client at the top. But what I would suggest is not to do that unless you need to. And where do you need to do that is if you have event handlers or if you're using hooks then you do have to have use client and we will have client components but this uh I want this to stay on the server. Now in order to get the ID since we can't use hooks on a server component you can simply pass in here and dstructure params and you can access the ID from that params. So here I'm going to say const and then I want to dstructure the ID from that params. And just to test this out, let's say room and then ID. So if I come back here, we see room three. If I go to this one, room one. So now we're able to get that ID. Now we're not dealing with a database or anything yet. We're just dealing with that that simple JSON file. So let's bring that in just like we did in the on the homepage. So import rooms from and then at slashdata slashrooms.json, right? And then to get the room that I want from that, let's create a variable called room. And we could say rooms.find find pass in a function with room and I want to get where the room ID is equal to the ID that I just got from the URL. Okay. And then we'll handle the case if there isn't a room. So if not then let's return and we'll just have um you know what we'll do is return the heading component and then we'll pass in title and we'll just say room not found. Okay we have to bring that component in. So we'll say import heading right and then if there is a room then we want to show down here uh let's get rid of this and inside the brackets or fragment whatever you want to call this. We're going to also have the heading but this is going to be the room name. So let's say title and let's set that to room.name. So let's just try that. So, I get room not found, which shouldn't, which actually shouldn't be uh Oh, you know what? Yeah, the money sign ID. Let's Let's put the money sign right here. There we go. Um, I actually think when we when we add app, right, we don't have to include this, even though it is technically money sign ID, but we'll just we'll just keep it there for now. And now the rest of this page. So under this heading, there's quite a bit of HTML and in classes. So if we go into the theme files and go to room.html, we want to get everything under this section, right? So this div with the bg shadow rounded every that and everything in it. So that ends down right above where the main tag ends. So, right above the main, I'm going to take that div and everything in it. So, up to right here, let's copy that and let's bring it into uh we want to go into our room page. Go right under the heading, paste that in, and let's change all the classes. So, class, I'm going to select one and then command or control shift L. Change it all to class name. Save that. And now it should look like this. Okay. Now there's a few things that we want to bring in up at the top here. So I'm going to bring in image since we have an image we need to display. I'm also going to bring in link. And then there's an I there's a chevron icon that I want to bring in as well. So it's going to be from React icons. Whoops. uh react icon slashfa and it's going to be fa chevron. Let's see uh chevron left. Yeah. Okay. So, we want to bring those in. And then let's come down here. So, right here we can replace this eye tag with the chevron. So, or with the fa chevron left. And I'm just going to give it a class name of inline and MR1. Okay. So that'll give us the little uh arrow. Now just to kind of go through and change all the static stuff, we'll do the image. So the img change that to image and the source is going to be put in some back ticks slashim images slashrooms and then we want the room image and then for this the alt we'll change that to just room.name name. And we do have to add a width 400. And let's do height height 100, which doesn't even matter what we put here because we're using classes, but it it will complain if we don't add it. So that should now show the image. Then we just want to keep moving down. So this here is the description. So get rid of that and let's add room.escription. Okay. Then we got the what do we have? Square feet. So this right here we're going to replace that with room.sqft. Then we have the availability. Get rid of that. And room dot Then we get the price per hour. So the 150 we're going to replace that with room dot price per hour. See that so far. Good. And then the address. So that change that to room address. Cool. And then the booking form that is going to be its own component I guess. Yeah. Let's we we're not going to make it work right now obviously, but let's let's take it and and make it its its own component. So I want to get it from this MT. So basically this div which ends here. Let's cut that. Okay. So this right here, we're going to cut that. And then we're going to create a new component. And we'll call this booking form.js. JSX. Let's do an SFC booking form. And then I'm going to paste in what I just copied. Save it. And I'm not going to do anything else to it. I'm just going to bring it into the page now. So we'll bring it in right here. So import and booking form and then just embed it right where I just cut it from. So slash booking form. save it and we should see the same thing. And then the only other thing is that this link, we want to use that wherever there's an a tag. So right here for the back button, say link and change the actual link to go to the homepage. So get rid of that. Just slash. And I think that's the that should be the only think. Yeah, that's the only a tag. All right. So we have our our page now. I think I think I've done everything I want to do before we actually implement apprite. And the reason I did this is so we could just implement the apprite database change a couple things to, you know, where we get the rooms from. Instead of JSON file, it's going to be from apprite using the SDK. But all this stuff we don't have to touch. We don't have to change anything in the in the component in the UI. All right. So, now let's I'm just going to close this stuff up for now. And we're going to head over to AppRight. And you're going to want to create an account if you don't have one already. It's free. Just sign up, confirm your email, and then go ahead and log in. And you should see your dashboard, which will look like this. And we want to create a project. Okay. Now, for everything that I name like the project, the database, collections, I'd suggest just using what I do just to avoid any confusion when we, you know, when we add it to our environment variables later. So, for the project name, let's call it book it and then project ID. You can let it randomly generate one, but I'm going to call it book it all lowercase. Okay, so that's our project ID. So, we're going to remember that. Click next. Choose a region which right now Frankfurt is the only one available but you can be notified when these other ones are. So we'll click create. Okay. So our project's been created. Now we need to choose the platform we're using. We have web flutter uh app. So iOS, Android, React Native. We're building a web app. So we're going to click that. And then for the name I'm going to do booket- app. And for the host name, I'm going to do localhost and click next. Now, this is showing you how to install the SDK, although we're not using this SDK since we're using Nex.js. Um, the one we're going to use is called Node- Appite. Um, so you could just ignore this right now. So, just click next and then next again and then go to dashboard. So, now we have our dashboard with all of our analytics, which obviously we don't have anything yet. So, the first thing I want to do is create a database. So let's go there and then create database and I'm going to call this booket and I'm going to also give it an ID of booket. Now from our database we want to create a collection. So create collection and we're going to call this rooms and I'm going to give it an ID of rooms. Okay. So now we can create documents in our collection. So if you're not familiar with like NoSQL um document databases, a collection is similar to like a table in a relational database and a document is similar to a record. Um so let's create a new document. I'm sorry. A document is similar to a table. Now our collection or our table needs columns or attributes. So let's say create attribute. And pretty much I believe everything we're going to create is a string. Um, so we're going to go ahead and just click string. And the first thing I'm going to add is a user ID because every every room that's created is going to be created by a user. And we need a way to link that user to the room. So that user owns that room. So let's say user ID. For the size, I'm just going to put 100. And we're going to say required. And then create. And then we're going to create the rest of our attributes. So another string. Let's call this name. Size we'll do 100. And required create. We also want a description. Description. I'm going to make it bigger. So we'll do 500. And then we want let's see address. Size I'll do 250 required. And then I also have location which is like the building or whatever. And and we'll put a little placeholder or something on the form so that they know. Uh size I'm going to do 250 for that. And I'm not going to make that required actually. And then we have what else do we have? Um let me just check my fields here. Okay. So we want availability size we'll do 100 and availability we'll make that required and then let's do the square feet so sqft size we'll do like 25 and I'm not going to make this uh I'm not going to make it required and then capacity so string capacity Capacity size, we'll do 25. And I'm not going to make that required. And let's do what else? Um price per hour. So price per hour and size we'll do 25. And I'm going to make that required. Then we have amenities. So amenities and I'll do let's do 250 for that. Actually, we'll do 500 because this is a commaepparated list. And amenities, I'm not going to make required. And then the last one, I believe it's the last one, is the image, which is going to be a string. Okay? And it's just going to be the image URL. Ultimately, it's going to be uploaded to storage using appite. And for this, we'll do let's just do 250. And I'm not going to make that required. All right. So, those are all the attributes. Now we can actually create some data from within this interface. So go if we go to documents and then create document. Um well actually before we do that let's create a user because I want to get that user's ID and put it in the document. So if we go over here to off and we say create user and then just add whatever you want here. You can skip phone. Okay. And user ID that's going to just randomly generate. So let's click create. All right. So we have our user. This is the use this user's ID. So I'm going to copy that and then go back to databases book it rooms create document. And I'm going to paste that user ID in. All right. And then for this other data I'm just going to grab from the JSON file. So we'll take this name and then we got this description. We got address. We got location which is like the building floor stuff like that. Availability is 9 to5 and square feet. We can just type this stuff in. Let's do, I don't know, 22 or 2,000. And capacity, we'll say 100. Price per hour, 150. Amenities, I'm just going to grab this. Okay. And then the image, let's just for now, we'll just say room-1.jpeg. All right. So, we'll click next. And we're going to create that document. And let's create one more. So again, I'm going to grab that user ID. And then for the name, I'll just go to this next one here. So, executive meeting room. We'll grab the description. Grab the address. Uh that's location. Address. Location is going to be this. And then availability 8 to six. Then we got square feet, let's say a,000. Capacity we'll say 75. Price per hour 120. And then amenities, grab this. And then image, I'm going to say room-2.jpeg. JPEG. All right. So, click next and create. So, now we have two documents. Now, this rooms collection, I want to go to the settings. And if we scroll down here, we can add permissions. So, the permissions that I want are for anybody. So, let's click any. I want anyone to be able to read. So, if you go to the the application, you can see the rooms. So, you don't have to log in to see them. But let's choose all G I'm sorry all users which means authenticated users and they can create read update and delete. So let's click update. And now we have the permissions. All right. So we have we have a rooms collection. We have our database. We have a user. I'm not going to do the storage just yet. We'll get to that later. Um but let's Yeah, I think we're I think we're good here. Uh oh no, we need to get the API key. So to do that, we go to the overview, scroll down to integrations, and then API keys, and we want to create a new API key. You can give it a name. I'm going to just call it booket- API- key. And I'll set it to never expire. Click next. And then I want to select all the different scopes. Create. And now we can click to copy this API key secret. And what we're going to do is create aenv in the root or env.local. And let's add apprite_appi_key and paste that in. All right. Now, there's a couple other things that I want to add for environment variables. So, I just want to go to the docs real quick. And if I search for let's say next_public, it's this environment variables. So we just did the key, right? U and that's the only thing that's not public. And I called it apprite API key. I guess we could call it this just to kind of just to stick with what the documentation says. So we can call it that. And then I'm going to grab these two. So we have our public endpoint. It's going to stay this cloud.apprite io version one. And then the project ID. Remember I said to remember that and that's going to be book it. So I have a few other things that I want to add to this thisv as well. Uh including the database ID, the collections, whatever collections we have and whatever um storage buckets we have. So we might as well just add that now. So let's copy that down. And this is going to be next public apprite database and that's also called book it. Then we got the rooms collection. So that's going to be rooms and it's going to be apprite rooms or I guess we could say collection rooms. And then we're also going to have a bookings collection. So let's say bookings set that to bookings. All right. And then we're going to have our storage buckets. So for this it's going to be apprite or next public apprite storage uh bucket storage bucket and this one is going to be for yeah we'll do avatars and that's going to be avatars and then we're going to have another bucket for the just the the room images. So we'll call this storage bucket rooms and this is going to be rooms. And then the last thing I'm going to add is just the URL. So it's it's not going to be apprite just next public and then URL and set that to HTTP local uh local host. And this will change when you deploy, but for now it's going to be localhost 3000. So that should that's all we need for our environment variable. So, we shouldn't have to come back here. So, let's close that up. Close that up. And the next thing we want to do is add, let's see, we want to add the client. Let me just see if I can find that the Yeah, SDKs. Um, SDK for node. No, that's not what I want. Initialize clients. Yeah, I think this Yeah, this is it. So, basically, we're going to have a config, a file in a folder called config. That's going to be our appreite client. And there's basically two clients that we need to initialize. One is the admin client and one is the session client. So if we look at this up here, admin clients should only be used if you need to perform admin actions that bypass permissions or unauthorated requests that bypass rate limits. And that takes in your API key. Then you have your session client. So this is used to make requests to appreite on behalf of the enduser. So this is where you you you're logged in and you can get your session and you would pass your session in. Well, in this case, they're getting it here, but I'm going to do it a little bit different and pass the session in. So, to create this again, let's create a folder. I'm just going to collapse the stuff. I'm going to create a folder called config. And in that, I'm going to create a file called appite.js. So basically whenever we do anything with apprite in terms of getting from the database or authentication it's basically going to it's going to come from this file. So let's start off by importing what we need. Uh well actually we need to we need to install the SDK. We didn't do that yet. So down here let's just run npm install. Now it's not going to be just apprite. That's that's for the client if you're building like a spa. Since we're using Next, we're going to do install node-app. Okay, make sure you do that. And then we're going to import up here node- appite. And I want to bring in here client because that's how we're going to initialize just like right here, new client. But there's some other things we need to bring in. If we want to use the database, then we need to bring in databases. If we want to use account and off, then we're going to use bring in account. And if we want to use the storage, we're going to bring in storage. All right. And then we're going to create two different functions that initialize again the admin client and the session client. So I'm going to do the admin client first. So let's say const and create. We'll say create admin client and set that to an async function. And I'll just copy that down because we're also going to have the session client. So session client and that's going to take in session. All right. So with the admin client, if we take a look at this page again, we just need these three things, these three methods. Um, actually, let's copy this this whole thing. We'll do Yeah, we'll copy this whole thing. And if you're not on this page, just you can just type it out or you can copy what I have in my in the in the repo. But I'm going to call this client. Set that to new client. Now, the endpoint, I have these or we have these as environment variables. So, instead of doing that, let's do process dot oops. env dot and then next. Let me just check what that is right here. So I'll just copy that. Okay. So that's going to be our endpoint. Then we have the project ID. So this right here I'm going to grab and we're going to replace this with process dot oops env. and then dot that and then the API key. So this right here say process env. All right. So that creates the client. Now there's a few things we're going to return here. So let's say return and then some curly braces. And we want to return an instance of the account. So we can say get account and then from that we want to return new account and that gets passed in the client. Okay. And we brought this this account right here that we're initializing we brought in up here. So now we when we use this file somewhere else, we can just import this uh this admin client and then we can use a new account instance. And we're going to do the same thing with the database and storage. So, I'm going to copy this down twice. And this one is going to be databases. And then this is going to be databases. Takes in the client. And then this will be we'll just do this storage. Okay. And then for the create session client, that's going to be very similar. So we can actually copy everything here and just go right in here and paste it in. Except when we initialize this client, we don't add the the key. So we can get rid of that. All right. And then we also don't need to uh return storage because we're not doing anything with storage with the session client. And since we passed in the session, right above the return, we're just going to check if session. And if there's a session on the client, there's a method called set session. We're going to call that and then pass in that session. All right. And now all we have to do is export. I'm going to export both of those functions. So, we want create admin client and we want create session client and save it. And that's it. We shouldn't have to come back to this file for anything. So, let's close that up. Close that up. And now, the first thing I want to do with AppRight in our project is replace this because this is just from the JSON file. We're going to replace it with the data from our database. And there's a couple ways we could do this. We could create API routes that we make a request to from our components. and in the API routes, use the um the AppRight SDK, but I'd rather use uh server actions instead of API routes. That's kind of the the new new and improved way to do things server side. And server server actions are just asynchronous functions. So, in the app folder, I'm going to create a new folder called actions. And in actions, basically, we're going to have a bunch of files here that are just single functions and they're they're server function or server actions. So this one, I'm going to call this get all rooms.js. And in order to use server actions, we need to add use server at the top because these only run on the server. And then I'm going to import the uh create server client a create admin client from config app.right. So the thing we just created. And then couple other things. I'm going to bring in revalidate path and that's just going to uh update the cache. So if we add a new room and we get redirected, we want that new room to show on this page rather than having to wait and refresh it. Uh and then if we want to redirect from the server action, we're going to bring in redirect from next navigation. So that's what we need to bring in. Then we're going to create an asynchronous function. So let's say async function get all rooms. And I'm going to use a try catch here. And in the try I'm going to dstructure databases await create admin client because remember in that file where is it config appite we return a new instance of a database called databases. So that's what I'm getting here and that databases has a bunch of methods on it to to deal with the database. So let's go ahead and fetch. We'll say fetch rooms. And we can do that with const. And then we're going to dstructure documents. But I want to rename it. I don't want to call it documents. I'm going to rename it to rooms. And we can get that with await databases. And then there's a list documents method. And what that's going to take in is our database ID and the collection. So first let's pass in our database ID. So we can say process.env dot and then I forget what I called it. This right here. So we want that. Paste that in. So that's the database ID. Then we want the collection which is the rooms collection. So I'll just copy that down. And let's do appreite collection rooms. I believe that's Yeah. All right. So now before we return the rooms I just want to let's say revalidate uh revalidate the cache for this path and we do that simply by calling revalidate path and then slash that's the path we want to revalidate and then we just also want to pass in layout and then after that we're just going to return the rooms. Okay. Okay. Now, if there's an error, we'll do a console log and let's say uh we'll say failed to get rooms and we'll also show the error. And then let's redirect um we're going to say redirect to a page at slash error. All right. So, let's save that. Now, I want to use this in our homepage, right? So, let's go to app page.jsx. And we're no longer going to use this. We're going to import get all. Um, wait a minute. Did I Oh, I don't think I exported it. Function get No, we didn't export it. So, down here, let's say export as default, get all rooms. And then here we're going to import get all rooms from actions. And then we should just have to uh right above the return we should just have to do con rooms. And we want to make this function async because we're going to use await and then get all So let's save it. Cross our fingers. There we go. So these remember these are the two I added in uh in app right now. The reason the images are showing is just because of how I named it. I called it room one JPEG and room 2 JPEG and those are already in the public folder uh right here. Okay. So remember we will they will be coming from apprite later. But yeah so everything is working so far. One thing I did notice though is I didn't put a key. So since we we have a list here, we want to add key and we'll set that to room dot and I think we have money sign ID. Let's just check the console here. Um oh I must have missed the class name. Uh where is it? Heading components heading. Yeah. So I have class here. Guess I want to just I must have did it somewhere else too. Room card. See class name. Oh, I got CL class name. So I'm going to select all those. Change Uh still Okay. So all the class names are taken care of. And let's check this. Those are the same things. All right. So we should be all set. So now we're getting the data from appite. Now, this the single room is still coming from the JSON file. In fact, we're not seeing it anymore because the ID is now different. So, let's go to our single file. Um, actually, before we do that, we're going to create another action to get a single room. So, let's create a new file called get single room in the actions folder. And we can copy what we have in the get all rooms because it's going to be fairly similar. So, we'll paste that in. So, we're bringing in all the same stuff. Let's change the function name here and here to get single room. And then uh let's see. So, same thing. We're getting databases. And then here, instead of dstructuring documents, we're just going to call this room. And instead of list documents, this is going to be a method called get document singular. And it's going to take in these two things, the database and the rooms collection. But it's also going to take in an ID which is going to be passed in here. So pass that in there. And then we'll pass it as a third argument here. And then we want to return room not rooms. And then we'll say failed to get room. And that should do it. We'll save it. Now we can use this action in our page which is going to be in the rooms brackets ID page.jsx. So instead of bringing in the JSON file, let's import get single room. And remember that takes in the ID and we're getting the ID from the URL here. So uh let's actually make this async. And then we can get rid of this stuff here and say const room equals await get single room pass in the id. And that should give us the room. Okay. Okay. So, if we save that, take a look. Great. Uh, conference hall executive meeting room. There we go. And this is not coming from the JSON file anymore. In fact, you could delete the JSON file if you want. Uh, I'm going to keep it there just so you guys have it, but it we're not using it anymore. Now, we're going to start on authentication. So, first thing I want to do is just create the login and register pages and forms. So, let's go to the app folder. And we're going to create a folder called login and a file called page.jsx in that folder. And same thing with register. So we'll create a folder called register and file of page.jsx. Okay. So in the register, let's do sfc. And I'm going to call this register page. And just for now, we'll just put in a div. and we'll just say register. Okay, same thing with the login. Let's do sfc. Call this login page and we'll just put a div and say login. Okay, so now we should be able to click on well actually we need to fix the links here because they're going to the HTML. So in the header component, let's change some of these links up. So let's see. And we might as well change the other ones as well. So for instance, booking slashbookings html. That should be just slashbookings. For add room, um, that's actually going to be room slash add. Then we have the login. So that's going to be slash login. And then register will be slashregister. We got my rooms. This is actually going to be slashrooms slashmy and then login again should be slash log actually that's the sign out so this doesn't really matter but we'll just put that for now then we got let's see that's the homepage that's fine bookings again so this is the mobile menu so just same same stuff uh rooms slash add and that should be Good. So now if I click on register, it takes us to the register. Login takes us to the login. Now for the forms, let's go to our theme files and I'm going to go to login HTML. And basically, we want to go to where the main tag is and we want the div that's inside the main tag. Not the main tag itself, but the the entire div inside of it. So let's grab that. Go to the login and I'm just going to replace this. We'll put some parenthesis and paste that in. Make sure we change the class to class name. Okay. So, if we save that, there's our form. Looks pretty nice. And then this right here, no account. This is going to go to slash register. And we also want to use the the link component. So, let's bring that in. So, import link and then see we're going to just change this to link. All right. So, there's our form. And then we might as well just add the register form as well. So, I'm going to just grab from the register HTML in the theme. Again, we're going to get the div that's inside of the main tag. which ends right here. So, I'm going to copy that and go to register. And let's paste that in. Change all the classes to class name. And again, right here, we're going to change this to link. And we can autoimp import it by clicking that. And this is going to be slashlo. All right. So if we go to register, we have our register page. Uh let's see what are we getting for an error here. Uh input elements should have autocomplete attributes suggested new password. Um did you mean HTML 4? Uh yeah. So for the four attributes, so I'm just going to select four equals and then command or control shift L to select them all and change that to HTML 4 equals like that. We want to do the same thing on the login. So let's see for equals select all of them. HTML 4 equals and I don't care about this warning here. We'll might deal with that later. But now what I want to do is create middleware so that we can protect certain routes. Now before we do the actual route protection, I just want to show you how middleware works with Nex.js. So I'm going to just close this stuff up for now. And what we do is create in the root, we're going to create a new file called middleware.js. And middleware is essentially just a function that has access to the request response cycle and objects. Um, so it basically sits between the client and the the actual request that's made and you have access to those objects and you can have it run on every request to any page or you can limit it to certain routes. And that's what we want to do is ultimately we want to have middleware that protects certain routes. For instance, the bookings, right? Bookings, ad room, my rooms, those are all routes that we're going to want to protect with middleware. So let's start off by just creating a simple middleware function that just logs the requests and then I'll show you how you can limit it to a certain page and then we'll get into the authentication. So I'm going to import next response here. Next response and that's from next server and then we just want our function. So, we're going to export an async function called middleware and that can take in the request object. And let's um I'm going to extract the URL path from the request. So, I can do that by dstructuring path name from the request object and there's a property called next URL. Okay, so I'm getting the path name and then I just want to log that path name. So I'll do a console log with back ticks and I'll say requested page and let's add the path name. Okay, so that's all I wanted to do. Now whenever you have middleware, you need to call the next method to continue onto the next middleware. So we want to return from this next response dot next. Now I'm going to save this and go to my console down here. And if I reload the homepage, you can see right here requested page slash. Now it also shows all the assets that are being used like the logo, the CSS file, um the layout and any images. But you'll see if I go to like login then we get requests for uh login /lo. If I go to register requestregister, so it's running on any whatever route we hit, it's going to run. Now to limit this to specific routes, we can just say export const and then a config object. And in that config object, we want to add a matcher. Okay, so matcher is going to be an array of routes that we want to limit this to. So let's just let's do login, right? So what this is saying is that this middleware is only going to run on the login route. So if we look down here, I go to let's say rooms the homepage. It didn't run. It's not saying requested page slash. If I go to register, it's not doing it right. This is just from before. But if I go to login now, it ran requested page/lo. So that's how we can make this only pertain to routes that we put in here. Now I know we haven't implemented authentication yet, but we can we can kind of mock it right now and just have a variable that says if we're authenticated or not. So let's create a route that we actually do want to block because right now we don't have any routes we want to protect. The only ones we have working are the register and login and the rooms and the single room. So, let's do the bookings. We'll quickly just create a bookings page. So, in app, let's create um we're going to have a folder called bookings and a page called page.jsx. And let's say sfc call this bookings page. and we're going to return just a fragment and I'll just say bookings for now. Okay, so we have our bookings page. Now I only want this to run, right? This middleware to run for bookings. So I'm going to add that in here. And now if I reload the page, we should see requested page bookings. Now I want to get rid of this functionality. We don't I don't care about logging anything. I want to protect this if it's whatever any routes that are in here. I want to redirect to the login page if we're not logged in. Now, like I said, we don't have actual authentication. So, let's just create a variable called is authenticated and let's set that to false for now. Okay. And then I'm going to say if is not is authenticated. Okay. So if we're not authenticated, then I'm going to want to redirect. So we can say return next response dot redirect and I want to redirect to we can pass in here new URL and then login or slashlo is where I want it to go. And then we want to pass in a second argument of request. URL and then if I go back to bookings and I reload it takes me to the login page. Okay, I can go to any other page you know but I can't go to bookings because it's this middleware is working for this. Now if I set is authenticated to true manually then I can go let me just reload this then I can go to bookings. Okay. So ultimately when we add our authentication we'll have the logic here to figure out if we're actually logged in. If there's a session and you know if we are then it'll show the page. If we're not then it'll redirect us. Okay. So we're ready now to actually implement authentication. So I'm going to do this with an action. Okay. So I'm going to go into actions and I'm going to add a new file here and we're going to call this create. Let's say create session.js. Okay, because that's what we're doing when we log in is we're creating a a new session. And basically, we want to submit our form to this action, our login form. And before we actually have it authenticate, I just want to hook the form up, show you how we can get the form data, and so on. So, it's a server action. So, we want to say use server at the top. And then we want to have u an async function called create session. And this is going to take in form data. And at the bottom let's just export default create session. Okay. Now like I said at the moment I just want to get the data. So we'll have we'll say email and we can say form data.get get and email. That should get the email input. And then we want the password. So let's do here password and password. And this pertains to the name attribute in the in the input. And for now, I just want to do a console log of the email and password. Okay. So let's go into our login form, which is going to be app login page.jsx. JSX and we want to import that action the create session. So import create session and we can have this submit directly to it. Right? So for instance um right here we can say action and we can set that directly to create session and that will submit directly to that action and we don't have to put a method it uh that will just react does that automatically. So now if I were to fill this out just put something in there and hit login. If I look down here, it's logging the email and the password. And that's from this the that catches the form data. We're getting the email and password and then logging it. Now, I want to use a a newer hook to handle to for error handling, right? Because we want to be able to show a a toast or a notification if the credentials are wrong or if they don't fill a field out or something like that. So, I'm going to be using a hook called use form state. Now, this is important. If you're using React 19 or higher, then it's going to be use action state. They work the same way, but after React 19, it's use action state. Before 19, it's use form state. And right now with Nex.js, if we look at React, it's using uh it's using 18. So, for me, it's going to be use form state. So, we want to bring that hook in. I'm going to go ahead and import and we're going to bring in use form state and that's actually going to come in from React DOM. Okay. And I'm also going to bring in the use effect hook. So yeah, I guess we'll just uh I'll put that Yeah, I'll put that right here. So import and then use effect. And of course that's from React. Now, if I save this, I'm going to get an error. And the reason for that is it says I'm importing a component that needs use effect. It only works in a client component. So, if you want to use any hooks, it can't be within a server component. And remember, there's server components by default. So, what we're going to do is just make this use client. If I do that, then that should clear up the error and I should be able to use this hook. Okay. Now, the way that we use this is pretty simple. So, we're going to come right up here above the return and we're going to say const and then state and then the the action or form action and set that equal to use form state. And then what we pass in here is going to be the initial I'm sorry, the the action that we actually want to call which is create session and then also the initial state. So let's say create session and then the initial state will just be an empty object. All right. Then what we want to do is for this action we're going to change that to this to the form action. Okay. So we'll just go ahead and put that in there. Um, and then whenever we use use form state in the action itself, it's going to take in the previous state as the first argument and then the form data will be the second argument. All right, so let's just see if this still works hooked up like this. So I'm going to go ahead and just input something here and log and it's still working. Okay, so again, we just brought in use form state. We set the state and form action destructure it from use form state. The action we want to call which is create session and the initial state which is just an empty uh empty object. And this state once we submit the form is either going to be a success true or an error. And that's why I'm doing this is so if there's an error we can show a toast or you can handle it however you know however you want to. Now, let's purposely throw an error. So, I'm going to go into create session, and I want to check, let's say, if there's not an email or there's not a And let's return an object with an error. and we'll say please fill out all fields. Okay, so we're returning this an object with an error if this is true. Now, right at the moment, we have required here and for the password. So, it's not going to let us even get to that point. So, let's just get rid of the required for the email temporarily. I'll put it back, but just to test this out. Um, now it's not going to do anything right now. Even if I do leave the email out, right? So, if I submit, nothing happens. Um, because we need to check for that. And that's where the use effect comes in in the in this component. So, we're going to go right here. We're going to say use effect. And let's pass in our function and our dependency array. And in the dependency array, we want to add the state. Okay? because the state is changing. If we're sending returning that error, that's going to go into this state. So in the use effect, we then want to check for state. And if that's true, then let's console.log the state dot error, which should be that string. This right here, please fill out all fields. And remember, this is a client component, so it's going to be in the client. So, I'm going to just reload this and let's try to submit without the email. And I'm just going to open up the console. And there we go. Please fill out all fields. Now, of course, we don't want to show it down here. We want to show it in a toast, but you can see how the functionality works. I'm returning an error from here. That's getting put into the state. And in the use effect, I'm checking that's whenever that state changes, this runs. And I'm checking for that error. and then logging it. All right, so hopefully that makes sense. Now, what I want to do before we move on is let's just set up our React Toastify so we have a nice toast message instead of a console log. So, we just want to install npm install react- toastify. And then what we can do is go to our layoutjs because we need the the container for the toast. So let's import and we want that to be toast container from React Toastify. We also need the CSS file which is I think I might have to look this up. One second. Uh let's see. Okay, so it's going to be react toify slashist slash and then react toy.css. I believe that's it. Okay. Now, this toast container we need to put somewhere in our layout. Doesn't really matter because it's positioned absolute, but let's just put it right here under the footer. And then back in our page, our log uh login page, we should be able to import toast uh import toast from React Toastify. And then where we want to call it is going to be right here. So instead of uh just doing a console log, let's change that to toast. And now if I try that same thing, try to submit without the email, we get please fill out all fields. All right. Now I'm going to put that required back in the email. But even if we don't have that, it's still going to still going to check it. Now, we'll also return a success if everything goes all right. Right? If we if we're able to log in authenticate, we'll have a success true that'll be in the state. So let's check for that as well. We'll say if state success then we'll show a success message. So toast.uccess and we'll just say logged in successfully. And then I want to redirect. So in order to redirect from a component, we're going to bring in the router. So we're going to import use router. And that's going to be from next navigation, not from next router. And then to initialize it, we'll come down here and let's say const router. Set that to use router. And right after we do the success, we're going to just call router.push. push and I want to redirect to the homepage. Okay. So now if we submit as long as the email and password is filled out then um let's do return an object with success and set that to true. Okay. Now this doesn't log us in. There's no login logic here. But just to test that out, make sure that we can send back the success. So, we'll just go ahead and fill that out. And we get logged in successfully. And it redirects us. Again, we're not logged in. That was just to test things out up to this point. Now, we can start to actually create the session. So, up at the top under use server, we're going to import and we're going to use create admin client from our app, right? And we want to create an account instance after we get the form data. So let's go. Yeah, we'll go right under this check right here and let's say get account instance because remember from that config file, the apprite config, we're returning an instance of the database, the account storage. Here we want to get the account. So let's say const account and set that to await create admin client. And then I'm going to open up a try catch. So try catch and I'm going to just this return success. I'll get rid of that for now. And in the try we want to first generate oops we want to generate a session. So let's say const session and we're going to set that to a wait. And then on that account object there's a create right here create email password session. and we're going to pass in our email and password. Okay. Then we want to create the cookie because we're going to have a cookie with the session ID on our in our client or in our browser. So for that um we're actually going to have to bring in cookies up here. So let's import cookies and that's going to be from next slash headers. Okay. And then right under where we create the session or right under here. So we're going to say cookies. So cookies parenthesis and then set. And you can call this whatever you want. I'm just going to call it apprite dash session so that we can identify it easily. Then it takes in session secret and then it takes in an object of options and I want this to be an HTTP only cookie. So we're going to set that to true. We're going to set secure to true. We're going to set same site to strict. And we're going to set expires. Um, and you can set this to something different if you want, but we can do new date. And then on that session object, we have an expire property. And then the path is just going to be slash. Okay, so the the root. And then after that is where we want to do our success or return the object with success true. Okay. Now if there's an error, so if something goes wrong then I'm going to do a console log and we'll say authentication error and then we'll just pass the error. Um, and I'm going to return an object with error because remember from this function we're either returning success true or an error with a a string. So that string here is going to be invalid. We'll say invalid credentials and that will show in the toast. Okay, because in our component we're checking for that error and it will show. So let's try it out. I'm going to reload and I'm going to do brad atgmail and I'm going to put the wrong password So I get invalid credentials. Now I'm going to put the right one in and I'm logged in. And now I'm actually logged in. If I open up my uh dev tools here and I go to application, we should see cookies right here. Appright session. So that's our session ID. Okay. So now we're able to log in. And again, that user is what I created here. So if you don't have a user, just create one here. Give it a password. And you can use that password to log in. So now that we're able to log in, I think the next thing before we do any um any protection or anything like that, I want to be able to log out and basically kill the session and kill the cookie. So let's do this. So, let's create another action. And we're going to call this one destroy cookie.js. And we could probably Yeah, why don't we just copy the Oh, did I say destroy cookie? I meant destroy session. So, cookie is going to be session. And then I'm going to copy what we have in create session. And then let's change this and this to destroy session. And we want to bring in let's see this time we're going to be using our session client because remember we have a session now to pass in. So instead of admin client we want session client and we're still bringing in cookies um because we want to destroy the cookie. And then this is not going to take in previous. It's not going to take in anything. So we can get rid of that. Uh we can get rid of we probably just get rid of everything else. I guess we'll leave the try catch though. So just get rid of that. And then everything in the try we'll get rid of. And then for the catch, get rid of the console log. But we do want to return an error if something goes wrong. and we'll just say error deleting session. Okay, so let's add the logic for this. So the first thing we want to do is retrieve the session uh retrieve the session cookie. So let's say const session cookie and set that to cookies and then get and remember I called mine appreite dash session. So whatever you called it that's what you want to put in there. So that will that'll get the cookie. Then we just want to check for it. So let's say if let's say if not session cookie then we're going to return an error or an object with error and we'll say no no no session cookie found. All right then we want to go into our try and we want to initialize our apprite client with the session cookie. So here we're going to say const and we're using account and we're going to await on create session client and this time we want to pass in the session and we can get that with the session cookie and then dot value. Okay, after we do that we can delete it. Let's say delete. So we can just we don't need to get anything from this. So we'll just call await and then account dot and then there's a delete session method that we can use and we want to pass in a string of current. So that will delete the current session. Okay. Next we want to clear let's say clear session cookie. So we do that with cookies delete and of course the name of the cookie which is apprite dash session and then finally we just want to return and set that to true and yeah I think that that should do it. All right so now how do we want to call this? we want to call it by clicking this sign out button. So that means we're going to go into the header. So components header and let's see how are we going to do this. Um we're going to bring the the action in that we just created. So import destroy session. We're also going to be using the router. So we might as well bring that in. I'll just do that up at the top here. So import use router and that's going to be from next navigation…

Transcript truncated. Watch the full video for the complete content.

Get daily recaps from
Traversy Media

AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.