In this lesson, we'll take everything we've learned about querying, inserting, and updating data in our Supabase project and apply it to a fully-featured application: a chat service similar to Discord.
Instructor: [0:00] With our selects and our inserts ready to go, as well as subscriptions for updating data you've effectively learned everything we need to know in order to build a real-time chat application using Supabase.
[0:11] Let's get started by implementing some nice quality of life improvements as well as some functionality that we haven't quite got to. First is some general CSS updates. I've already gone through, and I've updated the application with a number of CSS improvements.
[0:26] For instance, you can see here this auth page looks nicer. Then, when we log into our application, there is a header here which we'll make use of, as well as pinning the form to the bottom of the screen. The messages now have their own little boxes, which show information about the message, the content here, we'll add some more stuff here in just a little bit.
[0:47] All of this is straightforward CSS. I'll just zoom out here to 100 percent zoom, so you can see what it looks like without any sort of zooming or anything like that. In order to do that, I basically went through and added a bunch of CSS. There'll be a link to the GitHub in this exercise. You can go in and fill these in yourself.
[1:06] Now, what we can do is get started implementing some better quality-of-life improvements that make this application work closer to what we would consider a proper chat application. All of these messages, we have no idea who they come from, if they're all the same user, or if they're from any user in particular.
[1:22] What we want to do is, first, allow people to set their username. We'll give them a little form up here at the top for updating username as well as rendering username here in the messages. In order to do this, we need to understand that, at a more fundamental level, we don't know who the user is that's currently using our application.
[1:43] Here, in the code for sending a message, you can see that we referred to the user ID as session.user.id. This is the first instance of us referring to anything related to the user, the current user, inside of our code. This is stored inside of the session.
[2:00] As we've talked about a few times now, the user table here, which is where we have things like username and a public-facing ID, is not the same as the auth table, which is what we use to login. When we talk about session.user.id, that is our authorization table or authentication table, not our public-facing user table.
[2:22] What we want to do is, first, add support for looking at the current user inside of our code. To do that, let's go back to our useSupabase hook. What we have set up here is the session variable, which is based on supabase.auth.session.
[2:37] Then we also set up a on auth state change which says, when there's any sort of change to our authentication, we can update the session variable with that new information. What we want to do is set up another variable here called current user. To do that, I'm going to use the use state hook. I'll say, current user and set current user is equal to use state.
[2:59] I'll default this to a null value at the beginning. Now, we want to actually populate that inside of this use Supabase hook. To do that, I'm going to import use effect and then I'm going to call use effect function here. This is going to be an async function. Inside of that, I want to get the current user session ID, if it exists, and then use that to look up the user corresponding to that ID from my public.usertable.
[3:29] We can start by defining a new function called, get current user. This will be an async function. Inside of that, I want to check if session user ID. Notice here I'm using this optional chaining here which says if there is a session and there's .user.ID.
[3:50] If this isn't true at all, it will bail completely. If there's no session, it won't even try and run the rest of this code. Then with that user ID available, we want to make a request to Supabase to get the corresponding user. I'm going to say, await Supabase from user. Then I want to select everything from inside that table where the ID is equal to session.user.ID.
[4:19] This will make a request to the user table and Supabase, and specifically look for this user with this matching ID to our session user. I can get the data out of here which will be current user. I'll clean this code up a bit by moving some of these down into their own lines of code. Now, you can see I have this current user available if it finds anything that matches this value for ID. When we do a select, this returns an array.
[4:49] It's going to say, find all users that have this matching ID, even though this ID value is unique. There would only ever be one. There will either be no users or one user. We still get an array back. What we need to do is say, const found user equals current user, with an index of zero. Get the first user out of this current user array and that will represent our found user.
[5:15] Real quick here, just to be a little bit safer around found user, there may be a case where this current user array doesn't have anything in it at all. What we can do is say, if current user length, then do this code here. Otherwise, we'll just skip running anything here at all. In fact, maybe we can just return null.
[5:38] Now, we know in this particular section of the code, we have a user. It's our found user. Let's do something with that data. First, we want to return the found user. We'll say, go look up this user with a specific ID and then return it. We'll use that to set the initial value for our current user variable. We also want to watch for any changes to that user.
[6:01] If someone, for instance, is logged into their account and changes the username for their account, we want that update to persist into our current user variable. To do that, we'll set up another subscription. We'll say, Supabase from user, and I'll put this on a new line.
[6:19] I'll say, on update, that's our update event, which will send a function back with the new data. This will be a payload. Inside of this, we will say, set current user to payload.new. That will be our new user. Then finally, we'll call .subscribe to actually subscribe to that update.
[6:42] One thing that you might notice is that as we've written the subscription, this will update from any user in our database. If anyone changes information about their account, it will update our current user to that new value. This isn't quite what we want.
[6:58] One thing that's nifty about using Supabase is you can actually update this from call to only listen to specific rows. Instead of listening to every user, I want to listen specifically to changes to my user. The way that we can do that is by updating this to say, select from, and I'll change this in the back ticks, user ID equals .founduser.ID.
[7:23] We'll say, instead of listening to every user, listen to the row where the ID is equal to this ID value. That will narrow the subscription to accept updates or changes or anything from the specific ID. What we've done here, essentially, is first, made a request to Supabase to get the user for matching user session ID.
[7:46] If we found that user, we've made it available as found user, set up a subscription to that row for our user. Then anytime something updates, we will update set current user with the new payload data. All of this code still lives under get current user, which is an asynchronous function. If we scroll down here to the bottom, we need to call this function say, await get current user.
[8:11] This returns a found user, if there is one found. Const found user equals await get current user. I'll say, set current user to found user so that if it makes that initial request here to get the user data, I can set current user to that before any of the subscriptions fire, anything like that. We're essentially setting a default or initial value for our found user.