 Hey everyone, in today's video, we'll be looking at how we can build a full stack React application for a to-do list using Chakra UI and SuperBase. So the main focus of this video is going to be teaching you how to use SuperBase as your backend. So I might be using some snippets here and there for the UI components so that I don't bog down the video too much in terms of the CSS and the styling. So SuperBase is marketed as an open source Firebase alternative. And if you've watched any of my previous videos on this channel, you know that I use Firebase in a lot of my projects and that's because of the convenience factor. The fact that Firebase has so many features built into it like databases, authentication and storage. And SuperBase is basically the same thing and you have access to all of these different tools except that it's open source and that makes it awesome because then there's stuff like self-hosting like you can host your own SuperBase instance in your own Docker container. So there's no such thing as vendor lock-in and SuperBase actually uses Postgres in the backend for its database. So you can see that we have Postgres here and SuperBase is just like a layer on top of Postgres and it even helps us generate APIs automatically so that we have access to REST APIs and GraphQL and that's all automatic and SuperBase just generates that on the fly for us so we don't have to worry about all of those middleware and API stuff. So let's go back to our main application here so I can give you a demo on what we're building really quickly. This is my SuperBase console and you can see that I have a Postgres table here and it's empty right now for all of the to-dos that we're going to add. So let's say I add do the laundry here, let's hit add and you can see it's going to add it into our to-do list, task added and you can go back here in the backend and hit refresh and you can see that a new task should appear right here with an ID and the created time as well as the text. I can add more of course, let's say wash the dishes and you can add that and I'm going to refresh this and you can see that it's going to appear here as well. I can click delete to delete one of the one of those individual tasks and I can click clear task and we can just clear all of it in one shot. So that's pretty much the demo for our application, let's jump straight into the setup. We're first going to create the front end user interface components first and then we'll move on to SuperBase in the backend afterwards. So let's go to Chakra UI documentation and copy this line and run that. So I'm going to do yarn create react app, name of the folder, dot for current directory, that's just template and Chakra UI. So I'm not going to run this because I already did that and if you want to use TypeScript you can go ahead and run this, put this as the template name instead. I'm not going to use TypeScript in this course. So this is the directory that we're going to get the Chakra template. So I'm going to go and clean up some stuff now. Let's get rid of all of this unnecessary stuff. So we don't need all of that. Let's just delete that and then go to our app. We can get rid of this entire thing. We're just going to return an empty string for now in the app and get rid of all of that. And let's go to index and if you didn't notice I got rid of the Chakra provider here in app.js. So I have to add this Chakra provider back in the index.js. So let's add, we don't need color mode script, we don't need any of this. So let's go and do Chakra provider and then we have to wrap app in Chakra provider and we also can get rid of all of this. So get rid of that and you can go ahead and do yarn start but before I do that I want to go to package.json and add this browser equals to none. This is our environment variable. I don't want a browser to open a new tab every time I start the server. And I can actually go ahead and just add this in .env.local or local environment local environment variables. I can just do that and let's do yarn start and let's wait for the server start. So our server is now started. I'm going to go and open local host and let's see we get a blank template. So we're going to first going to have a heading component here that contains that heading that says to do list. And then we're also going to have an add task component which contains the input as well as the button for adding tasks. And then we're finally going to have the task list which is the actual tasks themselves as well as the clear tasks button. So instead of returning an empty string in app.js I'm going to paste this in here. Let's see what's going on. So we're going to have a heading here with that nice gradient background. And we have the size, we have the font weight, we have the margin and the padding. And I'm just going to change this margin to let's have it at 20. And then to do list is our text as well as our add task and task list components which we're going to import from dot slash components. And if I save this you can see that it errors out because this components folder doesn't exist yet. So let's go and see the error. I'm going to go and create that folder right now, components. Inside of this components folder we're going to have add task as well as task list. So add task dot js. And then in add task I'm going to paste this snippet here as well. So this is basically just a form with the input and the button that I talked about earlier. So input and button as well as the task list. I'm going to do task list dot js. So all of this code will be available in the GitHub repository. I'm going to link that in the description of this video so you can go and check that out. And back to this task list component. Basically we're going to have a vertical stack and inside of this vertical stack we're going to have all of our tasks, individual tasks and then we're going to have the clear task component which we're going to import from dot slash clear task. We'll do that later. But first, if you don't know what vertical stacks or horizontal stacks are, you can go to Chakra UI documentation and just like VStack stack. You can just stack. And you can look at what the vertical or horizontal stack is in Chakra UI. It's basically a way to help us stack our components nicely and align them nicely. And this is going to be super basic course so we're not going to focus too much on this Chakra UI stuff. So in this vertical stack, I'm going to have a lot of this horizontal stacks which are for the individual tasks. So let's say wash dishes, we're going to do that like five times. Let's save that. And then we can't resolve clear task that's going to give us an error. So you can see that and that's because we are importing dot slash clear tasks which is the button that you see down here, that clear task thing. So let's go and make that in components. Clear tasks.js and let's paste this in. So it's just a button and it's just that it's just a button here in clear tasks. Let's save and let's go back to our app. We still get an error that says component not found can't resolve delete task. That's because in our task list, you can see we are importing delete task here. And delete task is basically just that delete button for every individual task. So in the task, we have a text and we also have that delete task button. Just this delete task button you see right here. So yeah, we have to do that. Components delete task dot js. Then we're just going to copy this and paste. So it's just an icon button. We're not going to take in any ID for now. I'm going to get rid of that. It's just a simple icon button that says it's around. So let's go back and let's see. So this icon button is nice, but it doesn't have that icon. So ideally you're going to need a trash icon from something like react icons. So let's just use this icon here. And to do that, I'm going to have to install react icons. So let's stop the server yarn at react dash icons and just install react icons. Well, that's installing. I'm going to import this trash icon here. So import this trash icon from react dash icons slash fi. And then we're going to give this icon equals to fi trash 2. So there we go. Let's restart our server, yarn start. Let's go and refresh our web page here. Refresh and we should see the trash icon drop on this button. So there we go. There's one last thing I want to do before moving on to super base and that is there when there's no tasks in this task list. So I'm going to clear all the tasks and you see this image placeholder for when there are no tasks. So right now I'm going to go back to my task list. Let's clear all of the tasks here and let's take this and this and just delete that. So now we only have one task left. I'm going to comment that out. So right now we have no tasks. It's empty. And I'm going to come here and you can see that it looks like that. So that's not really nice. So what I'm going to do is I'm going to copy this code and paste it up here. So if there are no tasks, I'm going to return this box with an image. If there are no tasks, so placeholder image. So right now I don't have a way to check if there are tasks or not because we don't have our super base back end yet. So I'm just going to leave all of that code unreachable code here because I'm straight away returning this image. So let's import that image from Chaco UI as well as box and we also need this source image here. I'm going to take this image file. You can get this from the GitHub repository that I'm going to put in the link. So let's go and make a new folder here on the source. Let's call this images and I'm going to move this image over. So let me find that SVG and let's put this here. So we have this empty.SVG image and then let's go and import that image. So import IMG from dot dot slash images slash empty dot SVG and save that. Now let's refresh this so you can see that it's working now. We're finally done with all of the UI stuff and that's awesome because then we can start working on our functionality and our super base back end. So right now if I say do the laundry, let's hit add, it's not going to do anything because we don't have any functionality right now. So let's go ahead and add super base to our project. So you can go to super bases documentation and look for the quick start guide for React but I'm going to save you some time in this video and just tell you like what you have to do. So first of all, you're going to stop your server and do yarn add add super base slash super base dash j s. So that's the client here you can see in the documentation that we're installing for our project. And the next thing we're going to do is create a super base client file in our source folder. So let's go to source, let's do super base dot j s and I'm going to copy this code here and paste it here. So what you're going to notice is that the documentation exports super base as a normal export. I'm not going to do that. I'm just going to export default. So we can just import that as a default export. I'm going to do yarn start to restart the server and you can see here we're getting the API keys, the URL from process.env. So that's the environment variables. So what we have to do is we have to set the values for this environment variables in our .env file. So let me go ahead and copy this. We have to paste this equal to some value as well as this a non key. And where we're going to get this values is in our super base console. So much like Firebase, we're going to go and access app dot super base dot com. New project, we can give it any name to do super base dash to do. We can give it a super strong password. I need a stronger password. All right, so let's do that. Then we can use the free tier. Let's create a new project. Then we'll just wait for the project to finish setting up. Once you see this is completed, then we'll continue with the video. That is now done. Of course, we could copy the info here from this main page in the dashboard. Or we could also go to the settings section here, go to the API and get our URL. Let's put that here in our environment variables file as well as our N on key. So we get that and let's copy it here. So with that done, we have now successfully connected our super base back end to our front end in our react. And now I want to work on creating tables in our database for our tasks. So let's look at what the tasks requires. So each task is going to have its own text. And that's pretty much the only field that we need inside of our table. So let's go to the table section of our console, create a new table, call this to do's. And then I'm going to just leave the ID, make it unique. And then we can leave the created now as it is. And let's add a column for text. So this is the label for our tasks. We can use virtual text, either one. And then let's leave this, let's write like empty task as the default value. Let's see what's here. It's nullable. Yeah. All right. So let's hit save. And then it's going to create that new table. So our table is now created. We can insert a new row. We can leave the ID blank for it to generate it automatically. We can do wash the dishes as the text. Let's hit save. And that's going to add a new row to our table. So this is basically Postgres in the back end. But SuperBase has created an API automatically for us. And we can use that straight out of the box. So to do that, we're going to go to the API section. And let's look at the documentation for the code for the to do's table. So here's all the code that we can use to fetch the data. I can go and look at read rows. And so to read rows, we just need to use a select method and you can see here. So I don't need to read pagination with front tables. I just need the basic line. So I'm just going to copy that. Let's go over to our task list. So I'm going to paste it like this. But you're going to notice that this is an API call. So it's asynchronous. So let's put this code in an async function. Let's call this fetch data and then paste that in here. And this SuperBase is basically going to come from our SuperBase client. So let's import that import SuperBase. There we go. And I'm going to just check. So right now we can't just do fetch data in the middle of the function like that because we are going to have to wrap that in a use effect hook. Usually we typically do something like this with API calls. Let's have empty square brackets here so that it only execute once on mount and we can do fetch data like this. So here I'm going to rename the to-dos to tasks and then let's see what else. All right. So now we're going to fetch the task. We can do something like console.log tasks to see if everything's working. We can go back to our app. Let's open up the console. Where is it? All right. Let's refresh this and we get SuperBase URL is required. So let me check if my ENV local file, yeah, it's working. Maybe I just have to restart the server. I'm going to start and let's fingers crossed it will work now. Let's wait for that to load and I'm going to refresh the page here. So yeah, there we go. So every time you change the environment variables here, make sure you refresh and restart the server. So I'm going to see we have an array here that's an object. We have ID5 text wash dishes. So there we go. Now instead of just logging the tasks to the console, I'm going to come here and store the tasks in react state. So let's go ahead and create tasks, set tasks as react state and use state. And let's give it an empty array to begin with and I have to add const here. So instead of just console logging that will do set tasks and then tasks. There we go. And then now that we have that in state, we are going to just do implement functionality here. So I'm going to just uncomment this and let's do something like that. In fact, before I even do that, I want to get rid of this yellow squeaky lines here by making this return conditional. So let's do if tasks, if not tasks dot length, I want to go ahead and return this. So I'm only going to return this image if there are no tasks. And if there are tasks, I'm just going to return this and we're going to do tasks dot map. So task is going to have this. So let's do round brackets here instead. And then here we need to get rid of that same semicolon. And then here we just do task dot text. And the key will have task dot ID. Let's save that and it should work right now. Actually I think I pasted it in the wrong position. It should be here inside of the vertical stack. There we go. So let's go and refresh the page. And I can really see it's going to work, wash the dishes, and that looks sweet. And let's go create another task in our table to check if it's working, if it's really working. So clean the dishes, actually I already did that. So do the laundry, hit save, and then I'm going to come here and refresh. And we should be able to see two tasks right there. And now let's code in the functionality for the delete buttons. So I'm going to go to delete task dot js. You can see right here it's just an empty icon button. We just need to add like on change, actually on click. So on click we will do handle delete. And then of course we're going to make that function. So function handle delete. All right. So what we're going to put in this function is dependent on the API from Superbase. Let's go back to our Superbase console. And I'm going to go to API here. And let us go and look for the to do table. Let's look for delete rows. So right here delete rows, we can just copy this and let's go and paste it in our handle delete function. We have to import Superbase and we have to make this function asynchronous. So import Superbase. And here we can delete from to dos. And when you look at the equal column here, so let's delete the ID equals to some value. So let's do ID here. And you have to get that from the delete task props. So we need to know specifically the ID of which task we're deleting. And let's go to our task list. And here in the delete task, we're just going to pass in ID equals to task dot ID. And then what I'm going to do is I'm also going to add a loading state. So this is an asynchronous API call. So that means I can just do const loading and set loading equals to use state. And let's have the state to start off with false. So we begin by not loading the true before we call the API and then false after it's completed and just give this loading state to the button component. So loading. All right. So that should do it. And another thing I want to do is go to the Chakra UI documentation and use this toast component here. So after we're done with deleting or any kind of API processes, we're just going to show a toast to show that we can notify the user that the process is done. So the way we're going to do that is we're going to come to the documentation here. We can look at the usage. So we're going to import use toast and just make that using toast hook. So let's just do const toast equals to use toast from Chakra UI. And then here I'm just going to take this toast and put it here. So once we're done, we're going to do toast with the title of, if there's an error, then we're just going to put the error as the title. If there isn't an error, just going to say task deleted, the position we top, and then if we're going to get an error back, then we'll just use the status of error if not success. So you can look at the different statuses here. So the error is going to be red and success is just green, right? And then the duration and it's closeable. Okay. So now let's go back to our app and refresh this and let's just try out our delete buttons. So I'm going to just delete this, do the laundry, you can see it's loading. And once that's done, it says task deleted. Now the issue is that you still see, do the laundry here. And that's because I have to refresh the page in order to see the changes. So if we wanted to update the list, anytime we make any sort of changes to the database, we have to enable Superbases real time feature. So if I go back to the console, you can see that subscribe to changes section here. It says Superbases provides real time functionality and we can subscribe to the database so that whenever there's a change, then our application will just update automatically. Now I'm not going to use the syntax here because this syntax makes you handle the inserts, updates and deletes all manually and you have to manually check what's new and what has been inserted, what has been deleted. So I'm going to use an external library for this called Superbase Hooks. But before I go to that library, I have to first come and enable the real time feature in our table. So let's go to the database section here and click on replication. Under replication, click on the zero tables here under Superbase underscore real time. So let's click on that and look for the to-do's table and I'm going to toggle this so that it's on. So now we have a real time feature enabled on our Superbase to-do's table. We can go and install that hook that I was talking about, that library for real time Superbase Hooks. This is the documentation site for React-Superbase and basically all you need to do is install this package which I'm going to go and stop the server, the yarn at React-Superbase and while that's installing, I'm going to go back to the documentation and go to real time section and use real time. So this is the use real time hook that if we use this hook, then we don't have to manually keep track of what's changed like is there a new to-do's, is there a new task added to our to-do's table or was it deleted or was it updated? We don't have to care about that even though we can just manually do that from the API that Superbase provides us. We can just use this hook which gives us a much simpler syntax. So I'm just going to copy this line here and let's go to our task list.js and paste it right here and to do this, I'm going to have to import use real time of course. So import use real time and then let's do yarn start. And instead of having this asynchronous fetch data function and putting that in the use effect and all of that and the React state, I'm just going to get rid of all of that and you can see that we can just change this data to tasks and that's basically everything that we had to do. So you can see how much more simpler the syntax has become. We just got rid of so many lines of code and we just use this single real time hook and we can even get rid of the use effect and use state and we don't even need to import the Superbase client. But since we're not importing the Superbase client, we actually have to go and use a provider from React-Superbase inside of our app.js. So if I go to documentation, you can see that we need to have a provider that's wrapping the entire app. So I'm going to go to our app.js and actually I'm just going to wrap that in index.js here just right along with Chakra provider. So let's go ahead and import the provider. I can import provider s, Superbase provider, okay, and then let's just put it here, Superbase provider and we can wrap the entire thing and let's look at the documentation. So we're just going to give in the client in the value prop. So the client is basically just a Superbase client. So here I'm going to do value equals to Superbase and we have to import that. So import Superbase from .slash. So you just remember to wrap your entire app in the provider whenever you're using this Hooks library. And now I'm going to go back to tasklist.js to explain what the syntax of the use real-time hook really means. So here use real-time, you're just going to put in the name of the table for us, the name of our table is just to do, so I'm just going to leave that as that. And then what use real-time hook returns is a list of two objects. So the first is we can just name that as result, the second is re-execute. So I'm going to use object destructuring to get the properties out from the result object. And basically what we have is we have data, error and fetching. And what I did here was just I renamed the data property into tasks so I can use that specific variable name down in the code below. And error is just going to give us an error if there is any error. And then fetching is just a status variable. So if the hook is currently fetching data from the API, then fetching will return true. And once it's done fetching data, then fetching will just be false. And then re-execute is just a function that we can call to refresh data and we run the whole process manually. If you ever needed to refresh the data using like a sync button, and this is actually discussed here in the GitHub discussion replied by the maintainer, and you can just reach through this if you're interested, I'm going to link this in the description as well. So now I want to test out this code to see if it actually works in the browser. I'm going to refresh and we get an error that says tasks is undefined. So if you've noticed previously we did, we set tasks as a state variable and we initialized the task as an empty array in the beginning. But now we don't have that anymore. So we can't initialize tasks as an empty array. So sometimes tasks will be empty, it will be undefined because that's just what the use real-time hook returns to us and we can't do like task.length. So what I'm going to do is I'm going to say, if not task as well as task.length, then we can return this box and let's refresh and see if we get this error anymore and it works now. You can see that it takes quite a while to load. When I first refresh, it's going to be like an empty and then suddenly going to just show up. And I want to fix that by using a skeleton component from Chakra UI. So this skeleton component lets us load, have like a loading state for when we are fetching the data. So since we have this fetching variable from the hook, we can actually implement a loading state and just show the skeleton when it's actually fetching data from the AP. So here I'm simply going to just say if it's fetching, if that's true, then we're just going to return this skeleton component from Chakra UI, which of course we have to import as well. So skeleton and basically these props are just the styles for my skeleton. Let's go back and let's refresh this and we should see our skeleton when it's fetching data and that looks much nicer. So now that we have that, I want to test out if the real-time feature actually works. So let's go ahead and delete this, watch the dishes here. Let's hit on that and it's going to delete and you can see it automatically refreshes and updates even though we didn't refresh this tab manually. And I can go back to my table, I can add like, I can actually, I'm just going to go to to-dos table and let's insert a row, let's say I've added a new task. Let's hit save and it should automatically show up here and we didn't even have to click refresh and it just happens in real time. And finally let's work on the add task button because we are still not done with that yet. I'm going to hit add and nothing happens. So I'm going to go to add task.js and here I'm going to give like on submit to this form and put that to handle submit function. Let's just do function handle submit. And then we're going to make this input a controlled input. That means we're going to store the value of the input in the react state. So const maybe text and then set text equals to use state and then use state. Let's have empty string and then we're going to just do value equals to text here and then we're going to have on change equals to an arrow function that takes in event and let's set text event.target.value. So we're just changing this input into a controlled input. I'm going to go to handle submit, take in the event e.event.default. And then the next thing I'm going to do is go to our super base console, go to API and we're going to insert, we're going to look for a code that says insert. So I'm going to insert a new to do, a new task to our to do table. So let's look for insert rows and we're just going to copy this insert a single row. And then I'm going to put that here so we can see a way that means this function has to be an asynchronous function and then we have to import super base client. So import super base, I'm not sure why it's not showing up. So from dot slash super base, it's giving me an error I forgot from. And then let's see, so we're going to insert from to the to do table. So that's correct. And then we don't need some column other column. In fact, we just need the text to be the text. So it's going to be like text is text. But since we have a 16 text, you just need to write that once and it knows what to do. So we're also going to have a loading state. So let's have loading and set loading. And let's just set loading here, set loading to be true. And set loading to be false after we're done. And we want to default loading value to be false. And where we're going to set this loading to is the button. So let's go is loading equals to loading. And then I want to add a loading text here. So this is just a charcoal UI thing. We can have a text show up on the button when we're loading. So let's just put adding. And then let's see what else we need. So we're going to need a toast to show the user that when we're completed, when we're done with adding a task. So I'm going to go and steal some code from our delete task here. Let's steal this line of toast code and put it down here. So we need to import toast as well. Const toast equals to use toast from charcoal UI. So instead of task deleted, I'm going to say task added. Position is top, error is still the same. And then that's pretty much all that we need to do, I think. Let's go to our app and refresh this. Let's type do the laundry and hit add and hopefully it works. And it works. And another thing I want to do just to touch up the input a little bit is that if you can see I'm going to set this input to empty once we're done. I don't want to manually have to backspace all of that. So let's go down here and let's do set text to empty. And I'm going to want to change the input here to be disabled when we're loading. So I won't be able to change the value of the input when we're actually loading and sending data to the API. So let's refresh this, sending data to API. Let's just hit add and you can see it's disabled and it's done. It says task added and that's just beautiful because it automatically updates because we have that real time hook. So we don't have to refresh every time we make it. And you can even see that the loading text is working. So loading text and you can see it says adding because that's what I wrote for the loading text here in the button prop. So loading text. You can customize that and that's just a track review I think. So now we're pretty much done with everything and all we do right now is just code the functionality for the clear task button right here. I can go and delete all of these and you can see it works. So now that's the only thing that's left. Let's go to our clear tasks here and let's just do on click for this button and let's say handle clear. And then I already know that this is going to be an asynchronous function. So I'm just going to save some time and write async function handle clear. And basically you're just going to delete every single task and I'm just going to copy that code from delete task here as well. So this line here and take that and I'm going to put it here. So we just await super base from to do is delete and let's import super base. So import super base and you can see that we're going to equal ID equals ID which we don't need in this case because we're just going to clear everything we don't even need ID. So we just get rid of that and then I'm going to try this in our web app. So let's refresh this, let's clear tasks and you can see it gives us an error in the console. It says bad request for 400 bad request. So what this is is actually just super basis safeguards. So you can see here under the API documentation we have safeguards towards accidental delete and updates. So basically if we do any delete or update without accompanying filters, then it's going to fail because it's just a safeguard assumes that you're accidentally deleting your entire database and that's just catastrophic. So to bypass this, what you can do is we can just add the filter back in. So we can use equal or maybe you can use something like not. So if I go to the documentation for not, you can see it's basically like as long as the rows name is not equal to Paris. So something like that, it's kind of just a cheat like a bypass way to just get rid of. Just get over this small safeguard here. I can just paste this not here. So as long as the text is not equal to something arbitrary like do not delete me. All right, so now we can go back and test our clear button. So I'm using this refresh this, let's write clear tasks. It should work because we don't have, we already have a filter now and it should bypass that safeguard and you can see it works. But the thing is I'm not really happy with this because it doesn't have an interactive feedback. So let's just add a task here. I can like click clear task and nothing happens for a while before it actually updates. So let's just do like a loading state here and can like const loading, set loading, use state and then false. And then we're just going to do set loading true. We've done this a million times, so it shouldn't be anything for us. And then we're going to go this button here. Let's do is loading will be loading. And then we can also add a loading text here that says clearing clearing. All right, now I'm going to go back here and let's add some tasks. Let's just wait for that. Let's add two tasks. And we're just going to add another task called do not delete me to check if our filter is actually working. So according to this filter, it shouldn't delete this task because it has this text and the text is equal to do not delete me. So let's hit clear task and it says clearing. That's our clear text and this is the only one that remains. So everything is working. If I delete that as well and we go back to our table, we should see an empty table in our console, super-based console right here. And that pretty much wraps up this course. I just want to mention the GitHub repository, which I'm going to put a link to in the description of this video for the code that I've used in this video. And also if you have any questions, put them in the comments. I'll do my best to help and also check out my other courses on Firebase on my YouTube channel. Thank you and see you again.