In this chapter, I want to focus on making some improvements on our app. Primarily, I want to focus on resolving our complex usage of Vercel URL. While it's definitely handy that it's going to be automatically populated when deployed on Vercel, I dislike the fact that it doesn't include the HTTP protocol and then we just have a lot of complex ternary operations with defaulting to localhost and all of those things and I just really really dislike the way this works. Besides that, we also need to fix one issue that I found with our manual video revalidation from the previous chapter. We have to add the skeleton to the search page, and we have to add some default values to the search input component.
Let's start by resolving the Vercel URL environment usage. I want to fix that by using our own variable instead called next public app URL. It's going to be available both on the client because it uses the next public prefix and on the backend. And I'm going to manually add HTTP localhost 3000 here. Make sure that you add exactly what happens when you do bun run dev all, right?
If it says localhost 3000, use localhost 3000. And also take care of the protocol. If it's HTTPS make sure you add HTTPS as well and make sure that if it's not you don't. And now what I'm gonna do is I'm gonna go inside of my source and inside of my global constants here. I will remove this comment and I'm instead going to use the next public app URL.
And here's another thing I'm gonna do. Just in case you forget to add this default value or if someone clones the project and doesn't know this, I will fall back to localhost simply because I expect the app to not be modified, right? We use Next.js and this version of Next.js starts the app at localhost 3000. So it's reasonable to assume that this will be the default development URL. And now let's search throughout all the places where we use app URL starting with our search input inside of modules home UI components home navbar.
Inside of search input component you can see that we use this complex operation and the fallback. And we can now replace all of that with just App URL. This is the complexity I was trying to resolve. SearchInputFixed, let's go inside of Forms section inside of our Modulus Studio UI sections. Same thing here.
We are trying to create a full URL which then we are going to be able to copy, right? So we can now remove this comment and we can completely rely on app URL and then we will just assign the rest of the URL here. This is why it's important to have next public in front, because otherwise, if this was just app URL, you will not be able to read that on the client. So that's why it's important that we add next public app URL. Make sure you have no typo here in the next public part.
Great. We resolved the form section. Now let's go inside of the video menu and do the same thing. So the video menu component is located inside of modules videos UI components. Go inside of the video menu here, remove the comment and we can again just use a simpler version this time.
Besides search input, form section and video menu, there's also the one inside of trpc.client.dsx and this is arguably the most important one. Let's remove the comment to modify outside of Vercel. We can remove the console log if you've added it. And what we can do now is we can always rely on app URL. So our app URL from now on will include the localhost version.
So we can just add app URL here and we can remove the if clause. Like this. Make sure you remove the if clause, otherwise it's not gonna be correct. So a slight modification and a reduced complexity, because it's either gonna be localhost 3000, or when we deploy, we're gonna go ahead in our environment variables, wherever we decide to deploy, And we will change to whatever our public domain is gonna be. If that's www.codewithantonio.com, we are going to change that, add it here and redeploy.
That's how that is going to work. So I will add a little comment here if anyone is wondering. Crucial to modify in constants, actually to production domain, including protocol. So just in case you decide to continue building on this and someone doesn't understand what to do. So it's crucial to include that.
Let's go ahead and copy that one more time and edit here. Great, so we have this and now our app should be working just fine. So I'm gonna refresh a few times just to confirm. Here I am on the studio. Let's try doing a change.
So a mutation. A mutation is working well. Let's try loading all of my studio here. There we go. The RPC is working just fine.
And now I've noticed a little issue that I have. I have two videos and if I try to revalidate one of them I get an error. Something went wrong. The reason I get an error and you can actually find it right here so videos revalidate here's an error And it says that there is a duplicate value. Specifically, it says that there is a mux ID duplicate value.
There we go. Video is mux asset ID unique. And that's true. So let's go ahead and close everything here and let's go inside of our source database schema. So I did say that my asset ID, upload ID and playback ID as well as track ID are all unique because that is true.
They should all be unique for our video module. But what I didn't know is that when I go inside of my video procedures and then specifically go inside of our revalidate method, I didn't know that if we attempt to update the very record that has that ID, it will also throw a unique constraint error. That's something I didn't know. So this is what I will do. We first have to clean up unique potential, unique records, actually attributes.
So let's go ahead and first call this cleaned video. Await database, update videos. Set. And let's go ahead and do mux status back to, actually, we don't have to modify the mux status, we just have to modify mux asset ID to be null. Let me just go inside of schema so I allow this to be null.
Okay, let's modify mux playback ID to be null as well. Do not modify max upload ID. We absolutely need that. So we actually don't need anything here. We can just do this.
Also let's do where equals videos ID matches our video ID. Do we have a video ID? Or maybe I'm getting an error because this whole method is completely wrong. I just realized that I never added an aware clause. That's very dangerous.
I never added a where clause. Let's try this actually. Let's try adding the proper wear and then let's see if this will throw an error. Oh, that's a big issue on my side. Let's click revalidate.
Let's see. There we go. Okay, that was a big issue on my side. I forgot a where clause. In post-production, I'll do my best to try a little, try and give a little tip here.
But yes, we completely forgot that. That's why it was weird to me that that was throwing a conflict because it can detect that this is the very same record. It should detect that. There we go. That resolved two things.
So We no longer have the complex usage of Vercel URL and we fixed unique constraint issue with manual video revalidation. Now let's go ahead and let's add the skeleton to the search page. So let's go inside of our modules, inside of our search UI, sections, result, section. Because we already have the category skeleton section here, so let's go and focus on the results section. So I'm going to rename this the results section suspense.
No need to export this and let's export cons results section here instead. Let's wrap it in suspense from React and in error boundary, from React error boundary. And let's give both of this a fallback, like this and render the results section suspense. Now we need to go ahead with the props here. And what I realized we can do is we can just spread the props like this.
There we go. And Now let's go ahead and build the results section skeleton. So what this is going to do is first open a normal div, then simply prepare the desktop div like this. Then prepare a mobile div. So this one is only visible on desktop, this one is only visible on mobile.
And then go ahead and simply add five items and use the video row card skeleton. And in the other one, use video grid card skeleton. Make sure you have both of this imported. Let me just move these two things up. There we go.
And now we're going to use this here. There we go. So if you now go and search something, so this is called Untitled, so if I go to Untitled right here, there we go, I now have a nice skeleton. And let's of course check out the mobile mode as well, and mobile mode is loading nice as well. Perfect!
So now you can also see that this screen won't infinitely expand it has a nice limit right? So this works fine and now what bothers me here is that in my URL I can see the query but I can't see the query here so that's our last improvement I believe we've added the skeleton to the search page and now I have to add the default values to the search input component Let's go instead of source, instead of modules, home, components, home navbar, search input. And inside of here now, we have to add those default values. We can do that by using search params from use search params from next navigation. And then this is going to become the query.
So we can do const query, search params, get query or default to an empty string and pass in the query here. Like this. So now if you refresh you should see untitled here. One thing that I think will be a problem later on. I'm not entirely sure, but I think that if you use useSearchParams, you have to wrap that component inside of suspense.
So let me try and search for useSearchParams here about the documentation. And I will pause and try and see if I can find what I'm looking for. All right, so I purposely paused the video simply so we don't waste too much time, but here is the deal. You can use useSearchParams without wrapping the component in suspense but they do recommend wrapping the client component that uses your search params in suspense if you will be rendering this statically. How do we know if we are going to render something statically or not?
Well, we know that based on what kind of component that is. Now, our components, most of the time, will be using dynamic rendering, which is basically this, force dynamic. As far as I know, if you go into literally any of our page.tsx, you will find export cons dynamic to be force dynamic. Oh, it looks like we missed that instead of videos video ID. Let me just go ahead and add one more thing here because I think it's good to use this chapter for improvements to improve as many things as we need.
Add missing... What's the correct usage? This. Wherever we need. So let's first do that.
Let's add all dynamic to force dynamic, starting with our app video page here. Make sure this is force dynamic because this is dynamic page. This is not a static page. So the problem is that Next.js in deployment will in build will detect this and think oh this is a static component because it's not fetching anything but it is we are pre-fetching inside So make sure you add force dynamic inside of here. Let me check my home page itself.
It's force dynamic. Great. Let me check my studio page. My studio page also is missing force dynamic so I will add it here. My studio's video's video id page is not missing it, it's here.
Great. So we have this. Now I think we should also add it to the layout here simply because layout is the place where we are rendering the navbar. And inside of the navbar, we are rendering the input. So now that we have marked our layout as dynamic, I think it will cause no problems in our app.
We will see on the build, right? So I will add a little, let me find my layout here. To confirm this is needed or not. We will see this in the build step if that part is needed or not because I got a warning that since I'm using use search params here I have to wrap it inside of a suspense but at the same time when I tried building for the first time I forgot about adding all of these proper forcing of dynamic. So I'm not entirely sure what to tell you about that.
But while we are here let's also add const category ID, search params, get category ID, or an empty string. So now we also have the category ID. And what we can do now is the following. We can check if we have category ID and then URL search params, set category ID, category ID like this. And now if you search for untitled and click on comedy it should preserve both the search and the category.
So now I'm searching for untitled in comedy but if I click untitled in education this should disappear. Excellent And now here's another thing I want to do. I wanna go inside of my modules, inside of my search UI sections, results section. And I specifically want to refresh the results section every time either query or category ID changes. In order to do that, what we're gonna do is the following.
We're gonna add a key to look for a combination of query and category ID. Both of this will come from our routes. So now, if you change, you will see that a completely new request, which is not cached, will cause a skeleton, whereas the one that are already loaded will not. So if you select the new category, it will cause a proper loading mechanism. Excellent.
So I think that that's everything we wanted to achieve. Here's an interesting case. When I click all, my query ends up being untitled, but in here it's not. Oh, okay, I think this is unrelated. Yes, this is because we are technically on the search page.
Yes, this is, believe it or not, exactly how YouTube behaves as well. All right, so let me just confirm. If I click comedy from my homepage and then type untitled here, I should still preserve the comedy and switch to slash search. There we go. So that is working well.
All right. I think that those are all the improvements that I have marked for this chapter. We added the default values to the search input component and we added missing export cons dynamic to force dynamic. So that's 10 changes so far. Primarily, I want to make sure that you have added this environment variable, because you will need it in production and it's crucial to our app working because of the way our get URL method works right here in API or the TRPC.
So make sure you have that set up properly. Excellent. So now what we are going to do is we're going to end this chapter so we can finally focus on some other things like building our home page and then building these other types of grids. Amazing! Great, great job!