In this chapter, we're going to focus on creating studio videos. By that I mean we're going to create the basic video schema for our database. After that, we're going to push those changes to Neon Database. Then we're going to create Studio procedures so we can finally add video record record, sorry, creation, right? So we're going to enable this little button, which we created right here to actually create that record in our database.
And then we're just going to JSON stringify all the studio videos that we can load inside. Let's start by going inside of our schema right here. So inside of source, inside of database, inside of schema.ts. And inside of here, what we have to create is the videos. So let's go ahead and make sure that we have users defined, we have categories defined and now let's go ahead and let's export const videos.
So videos pg table. Let's copy the id which is going to be uuid. Let's give each video a title which will be a required text, so not null, a description which will be an optional text. Then we're going to prepare. Let's go ahead and How about we do this?
Let's copy this to created at an updated at so these are the ones which we almost always need and Now what we have to do alongside title and description is We basically have to connect each video to a certain category and a certain user. But the category can be optional while user is required. So let's handle the user first. This is how we're going to do that. We're going to add user ID and we're going to add UUID, and we're going to call it user underscore ID and then we're going to add a foreign key reference.
So references users dot ID. We are basically targeting the schema above, right? Users dot ID. That's why we knew that this has to be a type of UUID. Let's also mark this as not null so it's clear that this is required.
And since this is a foreign key, we can also give it specific instructions on how to behave on certain actions. For example, on delete we want this video to cascade. So in case user ID has been deleted for any reason whatsoever, all of their videos are going to be deleted as well. But we are not done with defining this relation. What we have to do now is we have to import relations from our Drizzle ORM.
So let's go ahead and do that. Import relations from Drizzle ORM and then we have to go ahead and do the following. Let's go ahead and go below our videos and let's go ahead and let's create export const video relations. Let's use relations passing the videos inside and then in the second argument you can destructure one or many, depending if you need one to one, one to many, or many, right, all of those combinations. And now we are going to type in the user to be one.
Users, targeting the fields videos.userid and references users.id. Like this. And we don't need many. Like this. Now, before we push this, you probably have some questions.
If we added this, why do we need this? Right? Well, thankfully, Drizzle ORM has very good documentation on this. So if you go to the documentation and go into relations here, they will explain to you what they are. So the sole purpose of Drizzle Relations is to let you query your relational data in the most simple and concise way.
Be that using their relational queries like this, or what we are going to do, we're going to use SQL-like query builders, like using joins, right? Now, instead of here, they go over all of their one-to-one, basically all of these things which we just did, like one-to-one, all of those other things. But if you click on foreign keys here, you get the answer to what we just asked. You might have noticed that relations look similar to foreign keys. They even have a references property.
So what's the difference? Well, foreign keys serve a similar purpose. Defining relations between tables, they work on a different level compared to relations. Foreign keys are a database-level constraint. They are checked on every insert, update, and delete operation and throw an error if a constraint is violated.
On the other hand, relations are a higher level abstraction and they are used to define relations between tables on the application level only. They do not affect the database schema in any ways and they do not create foreign keys implicitly. Basically what that means is that relations and foreign keys can be used together but they are not dependent on each other. We can define relations without using foreign keys and vice versa. This allows Drizzle to be used with databases that do not support foreign keys.
So I hope that kind of makes it clear and we can even try it out. So if I go ahead and comment this entire thing out, this will work just fine. And let's go ahead and do this. So I'm gonna go ahead and do Bonnex Drizzle Kit Push. So now I'm gonna push my schema and I actually have you know you can see it says pulling schema from the database and changes applied.
So if I go ahead and run my studio now let's see what's going on here. So I'm going to refresh this entire thing and I now have videos as you can see here and you can see that I have user underscore id right here and I have a proper relation to the user here. You can see that this user is not exactly a field, it's representing the relation And if I go inside of users, at the moment, we also have videos here. Right. So now let's go ahead and enable this.
Video relations like this, let's enable it and let's see what happens if I go ahead and do Bonnex Drizzle Kit Push. If I'm correct, this should not notice any changes and it did not do and it did not notice any changes. So these relations right here, they work on application level. They are used so that Drizzle, I'm not sure how to express myself, but yeah, the application level would be the correct way to express. It doesn't matter what database you use.
This is so the Drizzle knows when you are querying, when you're using your relations queries. Let me just go ahead and go back to this one. If you're using relational queries, and if you wanna do with posts true, you would have to add this type of relation if I understood it correctly. Which means that if we use joins, I think that we actually don't even need to have this type of thing here. Now there are two courses of actions we can take for this tutorial.
So if you want to, you don't have to type these relations right here. Though you would have to type them if, For example, you're not using Postgres. If you're using, for example, PlanetScale, which doesn't have foreign keys, in that case you would have to add these types of relations. The other reason would be if you're using relational queries. I believe that these queries cannot work if you don't have defined relations.
But if you use your normal select with joins, I'm 99% sure that you don't need this. Why am I even saying this? Well, I'm saying in case you're watching this far into the future where relational queries version 2 API has gone out. I'm now going to pause the screen and show you the GitHub pull request discussing the breaking changes which will come in the future in regards of that API. Here we go.
This is the pull request that they have ready at the time of making this video. So it's been opened last week, it seems, and these are the breaking changes that are going to happen, right? You will still be able to access the relational queries version 1 by using database.underscore query. For example, what does that mean for you? If for whatever reason you want to experiment with these types of relational queries and you want to use this exact syntax, you will have to add a little underscore here to access the version that I have in this tutorial.
But that really doesn't matter to you because we will not be using relational queries. We're going to be using query builders. But I just want to bring to your attention that there are some breaking changes coming and I want you to be aware of them both because it's a good practice to keep track of Dependencies you're using especially your ORM. That's a pretty big dependency for your project And also if you're watching it in the future, I want to help you in the best way I can You know for you to move along with the tutorial. So you can go ahead and try and Google this and you might actually find the pull request from Drizzle ORM.
And you can see some other breaking changes. So the following imports are moved from Drizzle ORM and ORM slash relations to Drizzle ORM underscore relations. So those are some of the ways you can access the relations from version 1 in version 2 when this comes out. But at the time I'm making this tutorial, there are still some things that they have to complete. So in our case, we are going to write this just so we learn how to write them.
But if you encounter any issues with this, If you notice, hey, this is throwing me errors, this doesn't exist. Remember, you are not going to need them for this tutorial. So I'm purely doing this in order to teach you how to write these types of relations which work on application level in Drizzle. And I think that you will be able to import this from slash relations in the future, I think. But you know, for now, if everything works, if there are no errors, no need to change anything at all.
One thing we're also missing here is to properly establish user relations here as well. So the foreign key's already done its job. It detected the user ID in the videos. And if you remember in our Drizzle Studio, when we clicked on the users table, we already had videos. But we have to add that manually if we plan on using the relations application level API.
So let's do that here. So const user relations will be relations users many and pass in videos many videos. Just like that. And let's do export const. Again, if it's running any errors, you don't need to write this.
Everything will work without it. And you can again, try it out. If you try and push something, it will just tell you there are absolutely no changes in your database. You didn't add anything new here, which is correct because what this does is just works on the application level. Great, So we have established a relation with videos and with users in two ways.
One, using database constraint foreign key which works because we use PostgreSQL. But in case you opt for using PlanetScale for example which doesn't have foreign keys or any other database solution which doesn't use foreign keys, this will be the proper solution for you. Alright, so we have that. And now let's go ahead and let's define an optional category ID. So category ID will be UUID category underscore ID.
And we will add references here as well. Categories.ID. Like that. So the difference between this one of course is that category ID is not going to be required. And if we want to add some specific action here we can do this.
In case the category gets deleted, what we can do is just set null, right? We don't have to delete the video if their category gets removed, right? That doesn't make sense. That's a bit excessive. We can just set the category to null because it's not required in the first place.
Whereas in here, I don't think, oh, it does offer us a null as well, but that would, I'm pretty sure, break the app. So cascade is the only option we can do here. Great, so we have that. Now let's go ahead and also populate it here. So we can just go ahead and copy this and just call it category.
One, categories, videos, category ID, and inside of here, categories ID, just like that. And let's go ahead and just copy the user relations here. And let's add category relations here. And I'm pretty sure it's the same thing. Basically, categories can have many videos like that.
Great, so we have this. Now, what I wanna do in the videos is well push this schema because we did add a relation with the category. So let's do Bonnex, Drizzle, Git, Push. So this now will cause some changes as you can see, but again, not because of this, not because of this, because of our category ID foreign key. So it's important that you understand that I'm probably being very repetitive now, but in case you're getting any errors with building with Relations API, you don't have to do it.
Just remove it from your project, nothing's going to happen. As long as your foreign keys are working and you're using the same database as I am, I'm pretty sure you're not going to have any problems at all. Great! And just to wrap up my babbling about RQB2, the docs will come out by the time this comes out. So if you're really really interested, you will probably be able to find it in the relations and you will probably see that this version 1.0 is much closer than 75% or it's fully out, which means that the version 2 of the Drizzle relations is probably out and you will be able to read all about how they're being used inside of here.
Okay, enough about me and relations. I hope that I kind of made it clearer. Now let's go ahead and once we've pushed everything inside of our database, let's go inside of our modules. Let's go inside of studio and let's create a server and inside procedures.ts like this. Let's export const studio router and let's use create trpc router from trpcinit like this and let's use create the RPC router from the RPC in it like this and let's go ahead and let's do get many and let's make that a protected procedure because only authorized users will be able to load their videos here.
So let's go ahead and do this. I'm just gonna go ahead and do .query here. Later we will add something else here, but for now I just want to demonstrate something. So what I'm going to do now is the following. I'm going to go ahead and get my data using await database from sslash database.
I will select everything from my videos schema. So I have imported that. And I don't think I even need to add... Yeah, I don't really care about the fact that I didn't query by the user ID for now, because that's not what I'm trying to show you. I'm trying to show you one cool little feature if we can call it like that.
So a very simple procedure here. Now we have to add this to our router here. So studio will be Studio Router. Like this. I like to order them by length.
If you think that's completely insane, feel free to not do that. Okay, so now what we're going to do is we're going to go inside of our source app folder studio, studio again, page.tsx. We're gonna turn this into an asynchronous method. We're going to do void trpc from server.studio.getMoney.prefetchInfinite. So I had no problems getting prefetch infinite but I will have a problem calling useSuspense.infiniteQuery and I'll show you why in a second.
So let's just add our Hydroid client here from the server as well and let's just add Studio View here like this. Now let's go ahead inside of our modules inside of the studio. We already have a UI folder so now let's open up a view here. Let's go ahead and let's add a studio view.tsx studio view and this is not the default export this is just a UI component. Inside of here what I'm going to do is just open up a div and we can add videos section here like this and let's go ahead and create our sections here and let's go ahead and add our videos section.tsx which is finally going to be a use client with videos section component here.
Videos section And now inside of the Studio View we can import the videos section component and we can go back inside of our app, studio, studio page and we can import the Studio View which is hydrated and it will have the cached and prefetched data. And if I now go inside of my modules, studio UI, sections, the video section right here, let me just refresh this to confirm that the videos section is being loaded here. If I now go here, for example, and if I try to do the following, I will try to get the data from TRPC, from the client. I will choose my studio and I will choose GetMany. Right now, it looks like it works.
Use suspense infinite query. Okay, I was trying to demonstrate something, but I failed. What I was trying to demonstrate is that if you name your query a certain name, I believe it will not always give you a strictly typed useSuspenseInfinite query. I don't know if you remember but this did happen to me in my very first procedure that I've created. So this was a big fail on my side.
I wanted to demonstrate something that is not true. So let me just try and make that happen. I'm gonna go inside of my TRPC. You don't have to do this. I'm just trying to show you something.
Let me bring back my hello base procedure here. You don't have to do this. So just a random query here and let me return. I've already forgot how to even write procedures, so I will just write this return hello world. Like this.
I think this is a normal base procedure. I think that if I try and change this trpc.hello, All right, no matter what I do, I cannot reproduce it. But in case you're having any problems with uSuspense infinite query not appearing for you, there is a fix for that. One fix would be just reloading your window but the second fix is what we are going to do. All right so I'm going to stop with all the explanations and demos and I'm going to remove this hello base procedure and I will focus by going back inside of the Studio Router.
Basically, what's important for you to trigger that useSuspendInfinite query in case it doesn't work for you is to add a proper input here. And inside of this input, you have to import Zod. So let's just go ahead and import, do we have Zod? Let me just go ahead and see. So Z from Zod, we do have Zod.
So basically what we need in the input is an object. This object needs to have a cursor. And this cursor can then be whatever you want. And once you add a cursor like this, you will almost certainly be able to get Studio, get many, and then you will be able to do use suspense infinite query. Right?
I'm not talking about this error. This is a different type of error. In my case, it was not even allowing me to auto complete use suspend infinite query because it noticed that it's not something that can be prefetched in that way. All right, so one important thing that is very logical, but still I've managed to mess it up. So I will give you a tip for that.
You have to be careful. If you're using prefetch infinite, you have to use useSuspenseInfiniteQuery. You cannot do useSuspenseQuery here and mix it with prefetch infinite or vice versa. Use just prefetch here and then mix it with use suspense infinite query. This will cause errors.
So you have to be certain about what you want. In 99% of the cases, if we do get many, that's why we're going to be concise with our naming. If we do get many, it means that we are going to do infinite querying, right? We're going to do infinite loads. So go ahead and add prefetch infinite here and also prepare ususpense infinite query here.
Now let's go back inside of our studio router procedures here and let's properly define this cursor that we will expect. It will accept an ID which is going to be a string and a type of UUID. So that's how we are going to know what's the last item we are left off and we are also going to need to have updated ads so basically another key to have this infinite loading cursor work on and this entire cursor can be nullish meaning that it's not required especially if this is our first request right the cursor will only be used as a paging method when we need to know from where to start querying and besides the cursor we're also going to have a limit which will be a number with a minimum of one and a maximum of 100 You can of course define this differently if you prefer it. And now this is an important thing. So inside of prefetch infinite here I'm going to add a limit 5 like this.
This also means that inside of my videos section, I have to do the same thing. I have to do limit 5 here as well. And there is one more thing missing here. Every time you do use suspense infinite query, you need two arguments. First one will be your basic input.
And the second one is get next page param, which you basically are telling React query, okay, how do I get the cursor? Well, that's gonna be last page next cursor. This will throw an error right now because we don't really return anything back from that query. So it's confused right now. So we are going to fix this in a moment.
So let's go back inside of this procedure now. But before we do it, there is one more thing. I just, I think I didn't finish my thought. If you add limit five here, you also have to add limit five here. So I think a good way to do this would to just establish some global constants.
So instead of your source, How about we add some, let's see, inside of my source, I will add a new file. Why can't I name this? I just want to add a new file and name it constants.ts, like this. So inside of my, in the root of my source folder, I now have constants and I will export constant default limit and I will make it five. So now that's what I'm gonna use everywhere.
Default limit from at constants like this. And I will do the same thing in the videos section, default limit from constants. There we go. Now let's go back inside of our studio procedures and let's go ahead and add a proper get many query here. So the first thing I wanna do is, since this is a protected procedure, that means that we can go ahead and extract from here, context, as well as the input.
So from the input, we can destructure the following. We can destructure the cursor and we can destructure the limits. But from context, we can get the user from the database and we can get the ID. But I like to remap it to user ID. You can do it like that or you can do it like this.
But I don't like having user here simply because sometimes I will fetch the user and I will name the constant user here and I will rarely need the entire object here. So I'm okay with doing context.user and then id user id or you can do like deep destructuring, I believe. So user and then this. I think that ends up being the same thing. For some reason, I like this more.
So I'm gonna use it like that, ID, user ID. This is also called an alias. I'm basically renaming this because id is a bit too generic. I want to know that this is the user id. All right, so now let's do a proper query here.
So we are going to query from videos but what we have to do is we have to add a where query and equals videos.userid needs to be our user id. So we no longer have to use the clerk user id here because this is an actual database user. It has a clerk id but we don't need it in this case. Let's go ahead and import equals from Drizzle ORM right here. And now what we have to do is we have to implement a proper pagination here.
So in order to do that we have to improve our where query. So let's add the following from Drizzle ORM. And, or, larger than, and I think that's gonna be it. Let's also add descending. Yes, so and, or, larger than, and descending.
All of these things are what we are going to need in order to do vagination here. So let's first wrap our very simple equals query which will simply load all videos from one user. Let's wrap that inside of AND like this and then we can add some combinations here. So if we have the cursor, that means we have to create the following query. If we have the cursor, let me just do or here and then just already prepare undefined here in the end.
And now in the or, this is what we're gonna do. We are either going to query by larger than, videos updated at and our cursor updated at, or we are going to query by and both, videos updated at and cursor updated at and larger than videos ID and cursor ID, like this. Let me just go ahead and indent these two. I think, yeah, there we go, like that. And this can be collapsed.
I just want it to be indented properly. Okay, I think this is fine. Great, so we have that. And now what we have to do is we have to add .orderby here descending videos updated at and descending videos id like that And we also have to add a limit. Limit plus one.
This limit comes from the input. So whatever we wrote, how many records to load, but we need to add plus one. Why are we adding plus one? Add one to the limit to check if there is more data. So we are always going to check for one more item than whatever the frontend requested simply so we can calculate if we actually have something else to load in the next batch.
So let's go ahead and do const has more to be if data.length, data being what we just loaded here. So if data.length is larger than the limit, in that case we have more. Now what we're gonna do is the following. Remove the last item if there is more data. So const items as more, in that case, data slice 0, minus 1.
Otherwise, just use the data. So if we do have more, we have to remove this extra item which we used to check to see if there's more, right? We don't want to bring that back to the user. The user is not supposed to know that we even loaded that. We are only using it for ourselves to know okay there is more data after what user just requested.
Great so we have the items. Now let's set the next cursor to the last item if there is more data. So now we have to set the cursor to whatever is the real last item, not this plus one, the real last item in this data which we loaded so that the next iteration knows where to start loading from. So const last item is items items dot length minus one a very simple array usage here and then we can define the next cursor. So if we have more in that case next cursor is going to have an ID of last item dot ID and updated at of last item updated at otherwise it's going to be null meaning that the cursor can be completely undefined there we go now that we have that instead of returning the data we can go ahead and we can return both the items and the next cursor like this.
And now, once you've done that, if you go inside of your Studio UI sections video section here, you no longer have an error here because we are returning the next page cursor. So this is the information that we are going to use to query for the next page. Great! So we just did that and if you go ahead and try now and do JSON stringify here and pass in the data, you will simply get nothing here. You can see nothing in the page params, nothing in the next cursor, nothing in the items, but you can see the structure which we have here.
So this is what we have to do now. We have to go back inside of the procedures here And besides get many, we also need to create our create procedure. But here's the caveat. I don't want to put that in the studio router. I want the studio router to only be responsible for loading videos on the studio page.
I want that to be a protected procedure so I don't have to dynamically pass in the user id. I just want this route to only be accessible if you're in the studio. So what I'm going to do instead is I'm going to go ahead inside of my modules and I will create a new module called videos and inside server and then procedures dot ts like this So I'm just gonna go ahead and copy this basic import here, like this. I will remove the, okay, actually I do need the protected procedure. I just think I don't need any of these here.
So what I'm going to add here is, first of all, rename this to video router. Or maybe it's called videos router. Yes, it's called videos router. So let's go ahead and add create here, which will be again a protected procedure. So we only want the author to create this.
So this will be a mutation. Let's get asynchronous context here. And we can destructure the ID from context user because we are logged in at this point. And basically what we're gonna do here is create the video. So await database, insert into videos, and pass in the values.
And we're going to pass in the user ID. The title will be undefined, untitled actually. And I think that's all we need right now. And in order for this to be populated here, you need to add dot returning. Otherwise it's not gonna return anything back.
So you can just go ahead and return back video, video, like this. There we go. So that's a very simple procedure which we're going to use to create new videos. So now let's go ahead and do the following. Let's go inside of inside of source, inside of TRPC routers here.
Let's add videos, videos router, like this. And now, how about we go inside of our source app folder, my apologies, modules, we have to go inside of the Studio Navbar, right? Components, Studio Navbar, actually Studio Upload Model, this is the place we need. And let's go ahead and add create to be trpc from the client dot videos dot create dot use mutation So just add this and then on click here, how about we just do create.mutate, like this. So what's gonna happen now?
If I go ahead and click create here, nothing should really change. But if you refresh, you should now see data here. If you're not seeing data, go ahead and check inside of the Drizzle Studio. I'm not running Drizzle Studio, so let me just do Bonnex Drizzle Kit Studio here. There we go.
So I should now have one video here. In case you cannot see anything here, you better check for logs here. You can see that I got post 200 OK for creating my videos. You might have gotten an error in your case. Otherwise if you can see something in the database in the videos section perhaps you did not connect it to the user properly.
You can see that my user now has videos. If I click here, I can see the untitled video here for my user. So if all of that is, if you can confirm in your database that all of that is true, but you still can't see anything here, then it must mean that you did something incorrectly inside of your studio procedures here, inside of the getMany method. So just double check that you've added equals videos user ID to this user ID from the context user, like this. Great, so we have that, but it's not exactly perfect, isn't it?
Because if I click create here, nothing happens. I have to refresh in order to see the next item. This was usually problematic in server components because this is where we are technically fetching these videos, instead of studio page.tsx. This is where we are fetching them, right? So it was always problematic, okay, do I now do router.refresh, right?
Do I do revalidate path? What do I do to make this fetch again? Well, here's the cool thing about our procedure. We don't care about this part anymore. We are only using it as the initial loader to prefetch something.
After that, everything ends up being on this client right here. This client from now on takes care of any type of revalidation here. Which means that if you go inside of your studio upload model here and you add utils from trpc.utils, use utils here, open up on success here and you call utils.studio.getmany.invalidate and you already guess what will happen now? I'm gonna refresh my entire thing here. I will click create, and there we go.
You can now see the changes happening in real time, leveraging both the prefetch from the server components for very fast initial load, and also leveraging the fact that the rest is done on the client, meaning you are familiar with that, right? This is not new to you. So we literally get the best of both worlds. I hope that it's getting clearer and clearer how we're going to use this prefetching thing. I think it's easier to understand it when you actually have some real-world examples.
So that brings us back to this point. Remember when I explained this to you, we use this server component to prefetch the data. But once we prefetch it, all this auto-refresh, I probably should have called this revalidation, not auto-refresh. So all this revalidation only happens in this layer here. It doesn't go back to the server component.
You don't need any router refresh or revalidate or revalidate that, or I don't even know what exists anymore. Right? It's all very familiar. Great. So I want to do a couple of more things before I wrap up.
So the first thing I want to do is inside of my Studio upload model is just disable this button. If create is pending. Like this. And I also want to do this. If create is pending, I'm going to use one icon called Loader2Icon from Lucid React.
So just to have... Oh, I'm also missing... This is disabled and this is supposed to have a class name of Animate Spin. So let's try it now. There we go.
This looks much, much smoother now. Perfect. And we can also do another thing here. So if I go inside of source, inside of app, layout, inside of my TRPC provider, I can add a toaster from components. Actually, I'm never sure where I have to import this from.
Okay, from components UI sonar. That is the place where you want to import this from components UI Sonar, don't accidentally import it from Toaster. So make sure you just add it somewhere near the children like this, then go back to your studio upload the model. And what you can do here is you can do toast.success and you can just say video created. And you can import toast directly from Sonar like this.
So now let me just refresh first. There we go, video created. And you can also handle the error in the same way. And what's cool about this TRPC thing is that you can actually be very confident about your errors here, right? So if you want, you can do toast.error, error.message, right?
And then if you were to go inside of your procedures, instead of studio server here, my apologies, video procedures, where you create yours, And let me throw an error on purpose. So through tRPC error, which I've imported from tRPC server here, let's see what happens if I just pass in the code, you know, bad request. TRPC error is not callable. I'll throw a new TRPC error, bad request. So let's see what happens when I refresh this.
If I click create, there we go. It just says bad request right here. You can also specify a message if you want, you know, specific message. So I think that now if I click create, it will give me a specific message, right? So you can go ahead and do that as well.
I'm only interested in what happens if, you know, an uncontrolled error happens. What if I just throw a new error here? Oops. Will that also work? Let's see.
Okay, so it manages to do these things really well. So I guess that we can rely on it, right? The reason I'm doubting this is because usually whenever I work with errors, they are never this reliable. Now they are always in some weird objects and I can often get some weird either JSON stringifications here or the app breaks. But I guess if the types say that this is a string that will always exist, I think we are in the clear.
But if you prefer it, you can also just do a generic something went wrong here and not use the error at all. Depends on how explicit you want to be with your users. Do you want to tell them exactly what went wrong? Do you think that's information that should be obfuscated for the user? It depends on the type of app you're building, right?
I'm gonna stay true to the source code and I'm gonna be using this, but keep in mind that you can use the error and then just get error.message inside of here. Great, So I'm gonna stop it here. There is one more thing we have to implement which we're gonna do in the next chapter and that is the infinite load because we have prepared our data for infinite querying but we did not do it on the UI side. And while I have you here, let's just learn this. What happens if I go inside of Studio page, and for example, if I forget to prefetch?
Let's see what happens then. So I'm gonna refresh this and everything seems to be fine. Yes, it's a bit hard to reproduce this issue, but I think I am getting the issue right here. And this is the issue, unauthorized, right? So let me try and refresh more and more.
You can see that I keep getting the unauthorized. I will try and add some space here to confirm. Am I still getting it? Yes, you can see that I am basically getting tier PC client error unauthorized. Why is this happening?
Well, it's happening because it lost the headers somewhere in between the server component and finally inside of my videos section here. So you have to be careful about this. Whenever you are doing prefetching, whenever you're doing useSuspenseQuery, you need to have the equivalent prefetch. This brings us back, you know, when I was explaining to you that the React query team has something in place called strict query or query strict wrapper, which will warn you, you know, on the project level, like, hey, you forgot a prefetch or you have an unnecessary prefetch, things like that. But you can always just do use query if for any reason you don't want to prefetch, right?
So if you don't want to prefetch, you can just use useQuery. But in that case, this changes the data, basically things get different then. We are gonna cover the normal examples as well. You don't have to worry. I'm gonna show you how to do all kinds of things.
And also, if you prefetch, you also have to change this to use suspense query because I'm not exactly sure what kinds of errors will appear. Let me see. I think that maybe the exact same error will appear. If I refresh here. Yeah, if you miss the wrong combination of prefetch, it's almost as if you didn't write the prefetch at all.
So if you prefetch infinite, you need to use suspense infinite. All right, I'm going to stop here. And we're going to do the rest in the next chapter. Amazing. So let's just go inside of here and let's use our handy tool.
We created the video schema, we pushed the database changes, created studio procedures and we added a video record. Amazing, amazing job! You