Roundest Pokemon - Full Stack w/ React, Next.js, TypeScript, PlanetScale, Prisma, tRPC, and Tailwind

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
anyways i have a fun plan for today since y'all are here early i guess you get to know a bit earlier than everyone else i'm building an app live with all my usual tech and it's going to be fun the plan is we're building an app to answer a question i've had for a while once and for all what pokemon is the least round excited to figure this one out once and for all i've been debating this with my friends for a long while there are a lot of pokemon that vary wildly in their roundness and i want to make an app where anybody can go to the website and vote on which pokemon is more around in which pokemon is less round and slowly via jury by trial of large numbers i will prove which pokemon is more or less round and i'm excited to do that anyways we are now live on twitch it looks like uh notification should be going out momentarily i am hoping to do this a bit more tutorially than usual but i'm also expecting a lot of questions throughout so feel free to actually i will go make a quick tweet and pin it into the space for anybody who wants to ask a question on twitter while i'm going but feel free to ask in twitch chat as well twitch.tv thea let's get started i am going to start in excalidraw to give a rough idea of what we plan to build i really like excalidra for getting ideas like this out quick okay here we have roundest let's name the app for now uh we'll say this is the main page for voting and this will have two boxes one will have pokemon one the other will have pokemon too we'll have little buttons underneath these roundest button a bit bigger select or which is roundest this is the general idea for the ui and there will also be a voting page are not a voting page a results page which will be a little more apple of his eye yes we are serious this is exactly what we will be wasting our time doing today i'll say pokemon one 95 round and i will duplicate this guy a bunch pokemon 28 is 75 round and pokemon 42 is 12 round etc i think this will be relatively fun to build we have a very clear here are like the two pages that we're making we need to persist data somewhere we need to create api endpoints to access and display that data and ideally we're gonna i would like to cache this page every minute or so so that we're not firing a new query every time somebody checks the results so with all that said i think you guys can guess what we start with init dot tips click this to copy to the clipboard tmux cd i'll make this a little bigger so you guys can see better cd code i have a personal directory i just named p paste that command funny i don't have that installed uh roundest mon now we have all this set up we have the edited uh typescript next.js repository with all of the like next.js defaults one of the first things i like to change we don't need to change any of these things i don't want to make things unnecessarily complex i do need tailwind though because i do not know how to develop without tailwind uh next js there's already a guide they have they like you to use their with tailwind in it but that doesn't have typescript so we are going to copy paste copy wait they it's always funny that like i feel like the junior developers are like ah you know an experienced developer will know exactly how to install all the packages they need when they start a project and it's like no we we go to the documentation too yeah i spend way more time reading about things than i do writing actual code and you'll find most senior developers are at a similar place like memorizing all of the places where all the configurations exist is a waste of everybody's time and effort yeah i like to move things to source slash pages but to keep things vanilla i will do this their way uh we're not gonna have a components folder uh yeah we are gonna have ah i really like to do source but whatever we'll do things the vanilla next js way to keep it simple i'm not going to break all the rules because i'm weird are you actually building an app that looks up the most round pokemon yes it doesn't look up the most around pokemon it's a voting app for us to vote on which pokemon is roundest it will persist all of those votes and eventually we will have a reliable source of data that provides us all of the votes that people had and lets us like sort pokemon by most in least round sounds like a lot of fun yeah an absolute waste of time unless i also make it a tutorial hence why we're doing this so yeah now we have the very basics of the tailwind stuff set up now i get to do my favorite part gut all of the other things it comes with but i don't like npm run dev now we have this on localhost 3000 and now i get to go delete all of their css and also include our global oh import i'll just throw that in the app that's fine we already have one so i can kill the styles global i will kill honestly this whole directory is now useless to me yeah i delete that one too if you need to do any of your tailwind customization you just do it in the config.js div class name equals t air uh text to excel font or text red 400 or 200 just to make sure it's working cool that is working yay we now have the basics i do want the whole page to be or have a background color though the easiest way to do that is an actual styles file so i am going to bring back i guess this is a fine start i'm gonna i like over committing in every sense i don't need to pick through this i almost always do git add dash p so i can pick specific things from the changes i make that i actually do or don't want to commit but in this case i just want everything so i'm going to hide the spaces on there if anybody wants to see that or has questions about who's talking let me know just want to keep screen real estate as available as i can get status git commit add tailwind and remove css modules cool i now wanted to set the background color as a permanent thing the quickest way to do that i will bring back the styles folder global.css i'm going to do this my favorite way which is find another project i did it in kind of recently i need the styles global and then i just need the colors and import io global dot css and now cool background and text color are set by default correctly nice and easy i can close this reference because i shouldn't need it anymore all right quickly commit that change status get add add default page styles oh i don't have a branch or a repo so next step everybody's favorite new repo on github no template i'll keep it private for now i'll make it public after roundest mon i'll make it public now why not not adding any things we already have the project local yoink yoink and there we go we're now on github which is cool in and of itself but where it gets way cooler is when i go to vircell make sure i switch over to my personal new project import deploy and now we're online well once the build is done which should not take very long thankfully set up next link we'll do that in a bit cool while that's the plan we can keep working so the next thing we should do is at the very least start sketching out the primary ui on this page uh we will i am trying to decide like general layout wise how i want to handle this i think i'll do h screen w screen flex flex column and just start putting things in here do you follow the build the ui first and do all the state stuff afterwards uh depends on where i tend to go after the thing i think is going to be hardest as early as i can so i can get that out of the way and know what like that the problems are going to be it also just makes it a lot easier to understand uh how do i put it it makes it easier to know where your time is going to be spent and roughly how long the whole process is going to take i also just saw physic or fizzog asked a great question what are we building for those who are tuning in later we are building an app called roundist which is going to be a voting app for people to vote on which pokemon is most around just an average day on webdev wednesday i saw that this deployed go to dashboard i'm going to give it a domain quick roundest it should take just a few seconds surprises take it even this long oh it's already done there we go hello world deployed on roundus.t3.gg now whenever i push it'll be there in roughly a minute i yeah for sale is great it's so nice being able to get something online that quickly with the ability to find an api or whatever as well just as easily so i wanted to test this at bg red 200 first bg pink 400 second just make sure everything is laid out roughly how i would expect uh it's full sizing it is a column so that makes sense uh yeah we can work from there cool uh i kind of want to justify center just defy center type is hard cool so here i'm gonna do like the which pokemon is rounder next 400 add text to xl text center kill background color good start this guy will be the like main voting section so we'll give it a border rounded uh p 8 just to make it nice and padded voting section the cat's already screaming at the door i will hope he stops him entirely otherwise i might have to go grab him yeah i need to grab him sorry voting section let's just put two fake pokemon in here for now uh flex justify and ah justify between that's what i would want here div i'll just do like empty divs for now div versus div and we'll make this class name with 16 h 16 bg red 200. the immediate thing i'm thinking is this feels too wide so max w 2 xl and line center middle this might be uh items center flexbox is hard cool but that means that it doesn't grow as much as it could flex grow right allow that nope that just makes it go that way yeah with rules are hard uh this will expand enough once these are bigger i'll add padding to this class name equals p eight uh item center cool generally this is the direction i wanted to go in this is coming together a little quicker than i expected uh one of like the theo hut takes i really don't like margins so if i have something where i would like more commonly put a margin i would rather just do a spacer element so here i want more space between which pokemon is rounder in this section so i'm gonna make a div between the two that's like that that adds that padding i like this because it lets you target specific elements and hide and show things a little more directly and it's not uh it makes these specific components have less side effects naturally speaking cool so now we have a very basic like structured ui my cat's knocking things over stop it bud i'm going to quickly commit this basic ui structure and buddy i just let you in you don't need to be out already cool i also love how fast these reload now that i'm using next 12. cool the next thing that's going to be annoying is the api so i say we we bite the hard hard chunk off all at once and dive straight into doing trpc trpc docs usage with next we will need to do prisma after but i just want to get an api structured and going first view uh okay import create react query hooks uh yep i will go make this utils folder uh i already want to break things up into source yeah i'm doing that source move this is going to likely fail to build see if i force a reset if it will that entirely broke because of the style loader maybe yeah i'm very surprised why or oh because i'm not purging that's the problem source i'm just going to do that and delete the components cool all right we'll let you out in just a moment but it's status get add commit and install more packages and restructure folders and now i can start making things the way trpc likes so now we're going to uh actually i should give an overview of what trpc does and why i want it for people who aren't as familiar cool so trpc the reason i like it is we want to define things that run on our server that the client has access to but i don't want to define all the you normally have to in between like an api schema a graphql endpoint or even really a service i just want to write functions and be able to call them on the front end but have them run on the back end trpc gives a really good abstraction layer for you to write a bunch of functions chain them through a trpc router and then have immediate access to those on the front end so trpc lets us build this significantly faster so we are going to use it to build significantly faster the cool thing about trpc is it doesn't actually generate code it just uses the type definitions from the code you've already written and the functions you've already defined to know safely what type definitions you're consuming on the front end so we're going to make the utils trpc.ts paste this here we don't have an app router yet they have a sample router here i will just yoink this for now uh i like making a backend folder for these things uh router index.tf cool i also like to have a path to quickly get to source i think we do that in we don't think we have to do that in package.json but we do have to do that in the ts config here we are that's compiler options right cool what this does is it lets me do at slash to get directly to source so you don't have to like dot dot all the way to where you're trying to go oh this is the trpc api is that what this is yes it is cool i'm yanking this part and we are going into pages api so for those that don't know the api directory in next.js is a little bit magic the super cool thing this does is lets you define an endpoint that always runs on a lambda and returns some json so in this case we're going to make trpc uh there's a specific naming at trpc and then in the brackets trpc.ts trpc.ts paste this guy i have to import this we'd have to export it here now we can import that and here is the api endpoint so what we did here is we created a router which has a query hello that takes in a text string that is null nullable this whole thing is also available and it returns greeting hello input.text world or world so if you put in text it says that otherwise it just says world so now to call this i first need to wrap the app with the provider so the way i like to do this part is okay so we already have that where's app type being used oh uh we already are doing that there app router hours is somewhere else so if i delete this cool type app router export default what is this all mad about oh because we have the multiple default exports there we go nice oh so somebody asked in the chat too like uh trpc is typescript for rpc i think you have a really good option yeah i have a hot take on this i've been pressuring alex to rename trpc for a bit because i think rpc's become a bit of a loaded term with the idea that it's like the the it you think of grpc when you see trpc which somewhat conceptually in terms of like it's a layer between these things it makes sense but the magic of trpc is that you have typescript on both sides so you implicitly have a type safe contract on both sides already trpc allows you to honor that contract through the process of defining an api endpoint without having to make an additional schema traditionally with something like apollo graphql i'll just find an example here quick with apollo you let's see if i can find apollo server yeah build the schema you have to create a schema which defines what different things return and if you create a server that fulfills that schema so you have your schema you have all these types but then you have to make uh the actual connection or the resolvers where for each one of these queries you now have to abstract specific functions with their weird apollo specific syntax that allows you to like define how the values this get actually are fetched and returned but now you have this abstraction between the method in which your code's being consumed and the thing that's providing the data that is getting to that layer and that separation is an additional thing you now as a developer have to deal with so i really like trpc for not making you do all this other work you just kind of write a type a type say function and then call it and that's why i like using it especially for quick stuff like this even for like bigger stuff like my company we're using trpc back to work so we now have this we have to make sure it works though so hop into our index const data loading equals u or trpc dot use query array and this should type or autocomplete why is this not autocompleting that's that's wrong interesting are we not importing the type and exporting that correctly what's going on there i have to do a restart of the typescript server too oh yeah uh that's not where that is this is in back end router nice and now this is type erroring because we're not calling the right query name so if i want to add an argument here it's in here i can type this and it'll autocomplete so text theo and we have is loading so if if is loading return div loading if data return div data dot greeting so if i now go here oh i can't resolve that because i haven't rerun this since i made all those weird changes to compilation cool hello theo and you'll see it doesn't even have a loading stakes it's actually doing ssr and doing that first hydration pass on the server so the client never actually has a loading state i can disable that in the uh app here by changing this to false and now you'll see it flashes loading really quick before going to thea i don't know how visible that is on the stream yeah it's visible on the stream cool but if i switch to true it actually waits until the server has finished loading all of those things before sending down that first pass of html which lets you have slightly slower initial response time but no weird pop-in and loading state on the client side because all of the data that might take time to load is hydrated on the server first so no more waterfalls cool looks like we have all these base parts working good status get add commit add trpc i'm going to push that and now i'm going to stop using this for silly demos and start using this for actual goals related to what we are building the first thing we should do i so one of the first thoughts i had is do i fetch the pokemon that you're voting between on the client or on the server like because i can just select two random pokemon at any point theoretically and the client's going to have to be able to do this anyways because you're going to want to do more than one vote at a time actually i think for that reason alone i'm leaning towards the client just picking two random pokemon yeah let's do that for now so the first thing we're going to do is make a get random pokemon helper export const get random pokemon equals i'm going to have this take an optional of not this one number the reason for this is i have to select two random pokemon and i would like to be able to skip if the one i select is one that i've already selected so const pokemon or pokedex number equals uh select random number from range javascript again to christian's point the most senior developers still need to google search most things cool that's what i was roughly thinking but see if anybody has yeah times max minus min plus min that's what i figured cool so i will join this guy we only really need this part i'm going to have const uh max dex count or max dex id equals we'll do 493 just because in the spirit of gen 4 and brilliant diamond shining pearl multiply this by maxed x id minus one plus one so that's never zero and actually just for testing purposes i'm gonna make this way smaller five uh if pokey deck's number does not equal not this one return pokedex number else we return get random pokemon not this one i guess i should define this as not this one number eventually this returns a number now we have this function that we can call however much we need to so export const get options for vote equals const first id equals get random pokemon cost second id equals get get random pokemon first id return first id second id so now we have this const options i'll just do first seconds equals get options for vote we import and we're going to put these here now div and he's a close seconds and if i go back to here we see the numbers but i have the awful background color so we're going to make this 800 so it's a little more readable four versus one and if i refresh three versus one two versus three two three three one v three i'm just making sure we never get the same number of both looks like we did okay with this also never seeing five which means i don't need to do that minus one that i'm doing cool looks like this part works nice 493 and now we have that this all looks good hit commit add random pokemon id fetcher now the next thing we need to do is get the actual data pokemon data pokemon github i feel like there's some open data source for this somewhere i remember where it is that's an android pokedex uh so just pokey api oh people things like eight days ago right pokemon api it is yeah like this api v2 pokemon one interesting i feel like i got a lot more resource here than i intended api v2 pokemon slash one why did this return everything oh is this like all of the game info too yeah game indices all the moves that can ever learn oh man can i select the less let's check the docs out oh they have a graphql version coming too that's really cool good for them i don't want to use that because i'm lazy node server side with auto caching type script with auto caching cool almost tempted to use one of these just for the type defs how maintained does that look very well maintained oh this is so tempting build failing yolo i'm voting yolo on this let's do it uh yolo yeah two potentially useful pokey node ts now we create a new trpc endpoint for i'm going to delete this query dot query get pokemon by id and this has an input z dot object z dot uh id z dot number and this needs a resolve resolve resolve href input return input.id just for now cool that works let's ooh i really like that trick for getting the images gram but i also need the name based on the number and that would be a little more annoying to just like rip from something using the the number.png method i've done that before for other pokemon apps though so really like that recommendation we are going to try out this thing though because it looks cool uh import i'm gonna do that separately pokemon clients const api equals new const pokemon equals await api dot get pokemon by id input dot id and now i have this type safe pokemon can i just return this and then on the front end i'm just going to console log this const first pokemon equals trpc.use query get pokemon by id it's going to type rx i haven't given an id yet we'll just give it id first console.log first pokemon.data if i did this all correct either we're going to get a type error because i'm trying to pass something that it's not friendly with or yeah i'm triggering a lot of re-renders right now and i don't know oh because this isn't a stateful yeah uh reacts.use memo i'll just import this there we go stable values yay and here we go all the data from that endpoint is being returned i think i crashed my chrome i absolutely crashed this tab and since i'm gonna close a bunch of these tabs actually those seem useful so we'll keep those open what the hell did i crash the ssr i absolutely crashed the ssr oh i know what's going on that's really dumb okay i have some thoughts here my guess is what's happening here is the use memo is not being honored as stable during ssr properly as such oh because that's uh ids update ids const first second equals ids there we go weird if i just turn off ssr yeah i'm gonna try that it feels like there's something going on recursively that i don't want to debug at the moment that's causing that to retry over and over and over unnecessarily i wonder if it's no this should all be stable hmm yeah now i'm a little more confused but we are going to turn off ssr for the time being to see if that helps it does cool that console logs is undefined because it's not trying to do the routing there and on the client test or server 80 client 195 that makes sense the quickest way to fix that would be to only make that change on client side not on server use state uh i saw the recommendation fizz uh you mean like here like that the only difference here would be so so the problem running into right now is this use state runs on both the server and the client so the server has different default values than the client does the quick fix for this would be export get server side props and in here defining the defaults and then passing those in as props and only updating after a vote that's like the right way to do this but i don't think i want to do this the right way at the moment i have better ideas for how to fix this later but i just want this working and to have confirmation i'm getting the data i want cool so name do we get a sprite we get a sprite sprites front default cool don't know how high quality those images will be but we can work with it we will do first pokemon second pokemon second if first pokemon dot is loading or second pokemon dot is loading return i'll just do null for now otherwise we know these exist which means i can do image source equals first pokemon to image or sprite our data dot sprites dot front default cool i'll do the same second pokemon uh i have these widths and heights set pretty hard uh can i just do 64. that's a decent size uh only problem now is the image doesn't fill uh class name equals w full cool and now we have this loading and fetching two random pokemon cool this is working i'm going to remove the bg red and do flex flex call because i want to blow these to a div first pokemon.data.name cool they're an easy way to capitalize just the first letter bucket we'll deal with those types of things later class name equals text excel x center cool uh p b four and that's not where i wanna put the bottom padding i'm gonna do that underneath here my usual i div class name equals padding or p2 oh tailwind has capitalized does it really uh they do actually yeah that is so good thank you why is taylor so great yeah let's just capitalize you can also make it responsive that's really cool i am going to put these above quick to see if i prefer how that looks i do not i want to make these closer though it feels like the fiji red 200. yeah that's what i thought uh bg red 500 yeah these sprites have so much padding that they don't need uh that's going to be annoying to work with uh do they actually though yeah they do these images have so much padding we're gonna do this the the super ugly way uh p t negative two rem i have to do m t that work it looks like that worked i hate that that works yep cool way closer yeah nice that was surprisingly easy so i'm going to commit this because this is good now get status get add commit back end with pokemon data and now i'm trying to say what i want to do next because there's a lot of like small things i think the first thing i want to do is minimize the amount of data we're returning because right now we're returning so much yeah let's do that first so right now we're returning all pokemon we don't need all of pokemon we need name pokemon.name sprites pokemon.sprites that's enough to start with make sure it doesn't break the type safety cool it does not so for those that aren't as familiar with uh the magic of trpc the super cool power that it gives you is the this guarantee of type safety so if i change this from name to like nickname you're going to see this immediately reads out because name doesn't exist even though this is server code that runs on my back end and this is client code that runs on the user the type safety isn't a consequence of a schema in between the frontend and backend it is the inherent result of using trpc as the method for defining our type our typesafe api we just get this as a consequence of the way the like uh library is structured which is super cool so i'm gonna quickly push this let's get add p get commit m store or send back way less data so for now i want to just get the like user flow working here so i am going to add const uh or send vote or vote for roundest guess this should take a selected number and in here to do fire mutation to persist changes but we also want to update to give you new options and now i can create a button button most round or rounder [Music] i guess i can just i guess not but for roundest first a tailwind ui i own a license this is okay application buttons code i like these button where do i do that should be there okay that scale is weird oh it probably has some with rule is it the inline flex no uh trying to fix the easiest way uh i guess i could do this not uh the default way uh where is here item center yeah cool that's what we needed to do are you gonna do a caption with you uh probably not good thought though that'd be a nice thing to add i could have somebody like push that if they'd like uh not my suggestion somebody oh sorry yeah i need to check chat more i have a different screen uh oh is this a video tracking for my multi-stream site sorry i just saw that message at dk i this has nothing to do with the company it's funny because yeah the main products themed round on uh roundup t3.gg is like the main product for the company that i built and round roundist is going to be this entirely unrelated thing i just but you're beginning to see the overlap though that there was some intention in this oh cool why is this failing uh oh blocked by course i know what's going on there and i have a really dumb fix for this uh let's go to my app get base url so what just happened there for those that are curious uh trpc is doing like server-side for us a lot of it to be frank and one of the things it does is fetch stuff for us but sometimes it does that is incorrectly but yeah incorrectly i think it's a fair word uh we have ssr fall so this shouldn't be too big a deal but i'm going to call get base url here so i've like a separate helper function i made to generate the correct url uh oh it needs the slash api trpc i'll just do link this bit uh this should fix everything uh trying to think what other i i do want to change this to be like the correct url because by default it might not be but it shouldn't matter too much yeah we're just going to do this as this see if that works i'll bug alex if it doesn't i'm giving it style buttons and fix trpc urlgen yeah kev chorus sucks funny enough as annoying as it is for even trpc it's way better with something like trpc than it is otherwise weird that it's trying localhost 3000 still uh vircell url should exist in the environment variables by default that's why that's the recommended path uh let's see if vercell has anything to say about this one oh it's already deployed the latest get okay there we go cool it was just the url generating incorrectly red now we have this on a website and if we vote on which one's router it will reload with different rounder pokemon i should have a better loading state uh it's a good next task actually ah yeah i'm trying to because this is the thing that we don't want to do uh trying to decide how i want to abstract this do i want to move both of these i kind of want to move those into a helper const uh pokemon listing equals i'm not going to react that fc pokemon i'm gonna do there's a really cool thing you could do with trpc uh trpc where you could infer from a type let's see if i can infer okay i don't have this here so i'm just gonna go again to my code examples i know in our admin panel i have yeah infer query response this is what i was looking for so this is a helper that alex wrote that's very copy pasteable i guess i should import the type here as well and that comes from trpc server so what this does is gives me a an infer helper that will yank out of the app router the type of a specific thing that we're doing so if i want type pokemon from server equals infer and this will do the usual typescript autocomplete that you expect from trpc pokemon from server oh this needs to be this is why i like react.fc for those that i've gotten in arguments about with this before this is specifically why i like it react.fc wait can you explain hawaii again because i missed it yep so react.fc is a method to like stub out a type for a functional component that comes with some expectations underneath the first one is it comes with children so props.children now exists and we know that even though i didn't define it because it's a consequence of using react.c the other thing that we get is if we don't return uh valid jsx we get an error so if instead of div here i return 12 we got a type error oh okay yeah i've seen there's some guy that i read that was like don't use reactive you know detailed reasons why but i think about yeah he's wrong it's fine it happens a lot a lot more nowadays recently or to be fair but yeah i i think that type safety is best done as inference and this is very much not inference it's very explicit but due to the way react manages props there are enough little gotchas that i prefer the separation yeah makes sense i had a i ran into that once where it's like oh i'm returning a jsx element but i didn't have verification for like props or something it was really weird that it would have been prevented with reactive scenes that sounds about right yeah i mean does reactive see of any other performance difference i don't think so right no it's it's a type definition so this doesn't affect anything yeah this is this is imaginary this disappears as soon as you like output the bundle oh okay so yeah i don't see i won't do why not then it's a preferential thing just some people's preferences are wrong props dot vote why is this not pretty earring oh import type react from react cool now i want to use this guy for the buttons here so i'm gonna do yeah try to decide how i want to handle the conditional here first pokemon dot data all right i'll do the first pokemon dot or not first pokemon dot is loading and not second pokemon dot is loading and and from here is where we do this guy pokemon listing pokemon equals first data actually don't know that this returned so property vote is missing cool my type safety is working vote equals vote for roundest first versus why is that doing that oh and you like this for a second now and seconds delete all this bit and now we should have a much better loading state a local host not much better i'm trying to figure out how i want to handle the the inherent size mess that occurs there maybe i want to move this over actually to the image i like that idea i don't like that oh this is w full that's why nice i think i suck at talking things out so thank you for that kevin yeah if anything i'm saying if i'm glossing over anything that's interesting or confusing and you want more detail please let me know but this is more like just generally i'm voicing my dumb workflow and hope that this is a good source of questions also like in the future if if you right now are watching a video recording of this later on and something i do hear is interesting or confusing to you always feel free to hit me up on my twitter handle at t3.gg spelt t3dotgg feel free to send me a time stamp or a clip from this video directly and say what is confusing or interesting about that spot and when i have a chance i'll be more than happy to get back to you and take a look i think it's time to start persisting so there are two persisting problems that we have to resolve here the first one is the persisting of votes so i'll just problems things to persist one votes to maybe uh data fetched from api so the first thing i'm like concerned about since the api that we're getting provided by what's it called words are hard sorry the api that we're getting from the what do we have defined in here the uh official like pokey node thing that's coming from the pokey api this is gonna get rate limited super hard especially if we're like the first people to use it this hard so i would like to back or to check our own database to see if we have the pokemon and if we don't go get that pokemon i er and then persist the pokemon that weekend i have a few ways i could think of doing this i also don't know if this is the right use of our time right now i'd much rather just start persisting the values that we're getting from votes so i'm gonna start with that problem actually yeah we're going to start with that let's start handling votes so first thing we got to do set up prisma and i like planet scale so we'll use planet scale along with it sign in github docs next.js see if they have a quick start i'll just do personal client quick start oh they want you to either starter add prisma to existing i don't know if i can switch teams e3 personal ready to create us west uh roundest yeah just create database and now we have a database being instantiated radical while that's going on i am going to get all things set up with prisma mpx prisma in it and we hop into here mysql they have an example model in here nope connect i'll handle that in a bit uh concepts i just want to see an example schema and they're not going to give me one so i'm going to look at my own schema because then i can have one quicker prisma schema let's find a basic relation this will be a good one to rip from the model vote none of these things exist uh we do need a unique id to be generated on this so we'll yank this guy id string created at so we can keep track of when votes occur uh voted for number voted against number i don't need to do that so just and yeah too used to javascript yo jc what's good cool this is a pretty basic but working data model now i need to set up the planet scale like dev experience so i like to do a separate dev branch and shadow i you have to do this i guess due to the way that planet scale works prisma likes to [Music] handle migrations as an abstraction from your model where they will create a sql migration that needs to be run and managing that against your own production deployment can be really annoying especially if you want to have your data protected so the other thing i need to create is that shadow so theo shadow region's the same this is being created now why do i need or why do we need both voted forward voted against because i want to be able to track if a pokemon is in a vote and didn't get voted for so at any point any vote is between two pokemon i need to know what percentage of the time one pokemon comes up it is voted for and if we don't keep track of who's in the votes then we don't no so yeah oh pokemon coliseum that was one of the hardest games i beat as a child cool i like to make a new like window to keep track of my p scale connections because one of the cooler things planet scale does is i don't have to run the database locally i can just connect to the branches i created there i also need to go into the settings for this and turn on migration framework prisma red now that is done i'm able to p scale round v one is for my project roundest oh i need to p scale org p scale org switch t3 personal oh it's round to stash mod yoink that's wrong paste 3310 and make this shadow yeah the prisma package for vs code is so good i absolutely adore it i have such a good time doing this having like the auto formatting like go ling style auto spacing for tab or such and stuff is so good yeah cool now that we have that everything's connected i should be able to set things up in the app now uh there is the next.js best practices thing for prisma next.js so we're going to go to back end and make a utils in here utils prisma.ts i totally installed prisma why would it not have that getting started maybe i have to mpx prisma migrate dev do the first generate oh i don't have the environment variable set uh this needs to be the localhost 3309 i think i did yeah 3309 dx prisma migrate dev i don't need to do the mysql mysql and now the shadow it's done the same way the key here being i have to do this weird syntax for giving it a root to connect with yeah at least a decade now which is weird px prisma migrate dev will this finally work looks good so far database migration name add voting model whoo real data from here we can have some real fun well it's ca cool no more type errors because we actually have a prisma client definition now so with this magic we get to have a lot more fun so let's hop over to the router router index.ts export app router this all looks good but we wanted to find something much more fun our first mutation so dot mutation first uh vote for pokemon or cast vote yeah i like cast vote input z dot object voted for z dot number voted against z dot number and async resolve i don't know why it really wants to do that every time return success true for now cool so now we have this mutation that you have to pass in these values and then it does something in this case we wanted to do some stuff with praise my const update or vote in db equals await prisma dot i have to import this let's import from the right place nope import prisma from at slash back in slash utils oh wait prisma dot here's where prince wig is really cool since we have this model in our database we can auto complete off of things that we know from our schema so in here we're going to get an error because i'm not passing the correct stuff here to create a new element this needs data but what i'm type erroring data type is the empty is not assignable to without uh it's missing voted for and voted against cool so if i grab input uh voted i can just do this actually because it's all the same which is really stupid that this works but it does which is magic cool vote vote in db so now we have an endpoint that will persist our changes let's add this all to our stuff right now just so we have it oh i need to not track the dot environment file so get ignore i know that entirely for now add prisma and basic endpoint for voting hit push this guy anymore i do want to make this work on the front end and then i have to go handle everything on the server and the database so this actually works forever so here we want the vote to actually do the vote so what we will do here is define the mutation const mutate or i'll just say fire vote or vote mutation equals trpc use mutation and autocompletes the cast vote and now i have this guy which has a dot mutate as well as a success and like data and other stuff within it so like voicemutation.data is the data if you fired one null if you haven't voicemutation.mutate this is what actually does the mutation here so what we want to do is if selected equals first vote mutation dot mutate voted for first voted against second else cool now votes should persist somebody redeemed find a cat give me one sec i want to make sure this works so if we did this all correctly i might have to yeah this is going to connect cuz i made those changes so i'll close and open this yeah that's connected and now if i vote ryolu chansey kingler ponita and i npx prisma studio four votes ta-da we're now keeping track of votes we did it good proof this works i want to have this work forever which means we need to add this stuff to the official like main branch promote a branch branch main cool buddy i might have to let him back out in a second yep now we can make a a deploy request can we make a deploy request branches this will be the easiest way create deploy request what this does is it takes the things that are different from our branch we were working on here in uh [Music] the p connection roundus mod theodev this is a custom branch just for us to work in during development so i put this branch through the migration that we did and if we add these changes to the deploy queue uh this is the equivalent of like a pull request but just for schema changes so here we can see the actual sql that gets output of create table vote id yada create yada vote for etc for all of the things that are necessary to actually create the sql stuff but we don't actually have to like do it which is what's so cool here it just all is generated as a consequence of using prisma and using planet scale and now this is going to deploy it might already have it just might be taking its time to update here because it's going across all the theoretical sharded instances yeah he's extra needy isn't that right but did that go through yet okay cool it deployed which means we are now safe to get status get add git commit make voting [Music] persist and i'm not going to push this yet because i want this to deploy with access to the database right now we are connecting to the database local using the planet scale cli in production you don't have a planet scale cli that's quite as uh easy to access and use so as such we have to get a connection url that can be used uh nation i'm looking for and seeing where we get the url url connection string aha this is what i was looking for cool so once again i need to not screen share because oh cool it does hide this but i don't want a screen share because i don't want any risk of this being visible uh we should now be good which means if i get status get push and now we wait for this build i'm going to close all these other things we don't need anymore github theobr groundest mon so we have this deployment which means theoretically if we go to roundist and keep this console open so let's see if anything goes wrong we've done that now and if i change this so i'm connecting to main instead just to see the main db data mpx prisma studio we can see yeah the votes appear in here and if i go and i vote more and i go back and refresh we have more votes we did it since when is an h.264 decoder not packaged with linux since always literally because h264 is proprietary so there's no truly open source implementation anyways connect back to p scale the theo dev branch so we can keep diving locally and not worry about things brad i still want to handle the database thing i also want to do some magic with next image to potentially cache that better yeah let's let's i'm going to make a list of the things i still want to do let's do that so first i need to make a readme readme.md oh i already have one cool roundest which pokemon is most round answering all of life's toughest questions delete all the rest i to do i want to persist data fetched from pokemon api i want to create the results page with counting sorting i want to use next image to handle image caching and better rendering i also want to maybe move images to the bulbapedia path that people were suggesting prior yeah i think that makes sense i'm trying to say the order of events i'm really curious about the image caching stuff and i kind of want to try this so let's start there uh this is the next image yep the band of my existence for those that don't know i am i battle next image pretty regularly it's a great idea but stumbles in his execution somewhat often so i am going to work through some of his rougher edges we have a pretty simple use case here like the image has a known size ahead of time and it's always placed the same way and it's always coming from one url this should be the ideal use case but i've been wrong before so we're going to start by switching over these images to next image so uh import image from at next or it's not even that next next image and then in here i'm gonna head out uh i'll be back probably in like 40 minutes see you later yeah i'll probably just kill the space and do the rest of this on github just so i or on twitch just so i have to manage all stuff thanks for stopping by yeah see you later cool space is done i'm just going to focus on here because things are going pretty well haven't we all battled next image i you say things like that but then versel doesn't fix it so i don't know if they've actually like if everybody's actually battled these problems or if we mostly ignore them uh that's invalid host yep need to go configure the valid hosts uh next.js image posts valid nope okay default okay so i want to add this i hate that i have to make a next config just for this next up config.js we don't want to use imagex we want to use default and we don't want that we want this url yeah i'm not surprised the audio here is gonna be much better i was using a crappy lav mic on spaces which i can now drop which is nice and on here i'm using my wave 3 with like audio processing and compression that i manage myself uh will this work now i'm probably gonna have to reload the builds yeah i made config changes why is this mad oh like capital d defaults capital d and the docks you i i it really feels like image gets no love at first invalid source prop because of the https really thank you kevin it said you can do the slashes in the docs it literally has an example with slashes what sorry why in the docs why in the docs is it https if i can't use that or even the path after what why why the do they call this path if it's host this isn't a path this is a host now the way they have configured this is incorrect and i am angry it is configured under images in my next config.js though that valid there are other options for this like what the hell does it do custom domains first is that all i have to do that's really dumb domains this is so stupid what does this come out 216 ram which is 256 pixels that's fine uh with equals 256 height equals 256 cool let's look at more next image stuff to those with layout fill what are the other layout options oh fixed and i'm just going to set fixed on this instead see if that works layout equals fixed fill that that does what i wanted to where's the github uh i should link that at the bottom i'll add that quickly actually good call uh github theobr roundest mon i'm gonna quickly add this though and get commit use next image loader and now relative give class name equals sticky b or bottom zero with full border aahref equals github link github okay that is trying but not hard enough uh absolute should this be a grade of some form yes but uh we will get there when we get there absolute bottom zero w full probably don't actually want the border text excel text center pb 2. good enough you now have a link to the github on the site it's status add p hit commit add github link to bottom of site hit push yeah i am familiar with that very yeah that's an old classic nonsense mapping experiment and that is definitely like a subconscious influence on what i'm doing here by asking a question as abstract as which is rounder and expecting people to find meaning in this themselves i am curious how much better the deployment feels now that we've gotten the next image caching yeah these are being cached on our server now cool so that solves the image data problem i did want to try uh i remember gram linked earlier a syntax to like break the urls from bulbapedia which interestingly the link he sent which did work before is now forbidden so i don't know how reliable that method's going to be if you have a way for me to generate a url for ebola a bulbapedia image such that i can x like externally access it so the problem there is i'm not that link isn't to the file and when you click the link to the file there it comes with cookies that you get from having been to that page before you can't directly link to that file in like a a non-white listed uh like accessed thing like the first link you sent me to the zero zero one did not work uh here actually i'll just twitch tv theo just to prove this live we're going to okay this is muted cool can i scroll back far enough i can't so i'm going to paste this link that i have on my desktop right now one of the ones you sent before in the chat i'm going to click this oh wow infinite recursion click this here and you see 404 not found but if i go to the file link first so i go to this but for zero zero one and now i go back here and refresh interesting i thought that would work okay what's different about these urls interesting okay the urls are entirely different depending on yeah so this one was b b0 this one zero zero three zero zero one so these actually have different paths fundamentally which makes it significantly harder to scrape also these icons are quality so i think i'm gonna stick with the ones that we have right now knowing that in the future i can handle that better rad so we're doing images somewhat correctly right now it's definitely loading a lot faster as a result and once one image has been loaded once that's now cached in our like cache layer forever so this should be fast enough and get faster over time which is cool so red just feels definitely rounder oh i think butter freeze rounder clamp pearl's definitely rounder giraffe rig is rounder his tail oh he'll be mad if i don't say little bunnies round mewtwo is pretty round shoulder's slightly rounder snorlax is way rounder well that was a tough one chrome circles on his face it's pretty round ooh so i didn't download these images dk these are coming from pokey api which is this and rather than fetching directly from this on the back end and like foot or forwarding things i found a cool helper package called uh what was it called uh pokey node ts and this creates this api instance rename this for clarity pokey api connection and this uh will fetch the pokemon by id from that like public api and give me a type safe return that i can then use as i need to for whatever i want to do with it which is super cool the thing i want to start doing though is keeping a decent bit of this data on my own servers simply because it makes doing transforms against the data much easier and i won't have to they make like hundreds of fetch requests to get lots of things at a time i'm also curious can i do it does get pokemon by id take it just takes one id it does not take an array can i okay getability by name getability by id characteristic yeah i kind of want to write like a backfill theoretically we should do different you know or unknowns for each letter but no no so right now my gut feel is it would be a good use of time to start persisting these i kind of just want to write a script that does it quick yeah let's do that uh get status get add p get commit better naming so i don't forget that change and now i want to make a script so i'm going to make a higher level up here scripts also gonna try and remember how i handled this in here i have in package json oh i deleted this change before i oh i know uh there's open source raid guild actually has the scrivener changes that i was helping with i'm package.json aha so the reason i'm looking for this is i want to be able to use typescript in ts node to easily run a script so i don't need this open anymore i'm going to close it so i stopped making that mistake package.json adding the script here npm install save dev ts node ts node lets you run like typescript code in dojs pretty trivially so now that we have that i want to update the model to include the things we need it to so we're going to hop into prisma schema we're going to make some pretty significant changes here model pokemon pokemon has id you don't really have created that for this doesn't matter uh i kind of want to make this pokedex number but it's going to be the same either way so we should just leave it as id uh name string image or sprite url string and now i want to link this to a pokemon uh let's look at some relation examples i'll just pull up my own schema again ah so these should both be ins and this id needs to be an int as well [Music] we will not provide it a default voted for id voted against id voted for pokemon at relation fields voted for id references id and this is okay so i guess messages is probably the better thing to compare with i guess that votes for votes against pokemon relations are hard oh howdy other theo good to meet you man so the way i handle this here that relation references id yeah the problem here is that like a clever way to scrape all the pokemon db images i might end up doing that in the future for now i am happy with next.js and versailles caching the images for me from wherever i'm like catching them from with the api so i'm going to stick with that for the time being but that is a good solution for near future what i'm trying to decide is how i want to manage this relation where i am creating a vote entity that is linked to two pokemon one is linked as a four and one is linked as an against and i want to keep the data model in the vote roughly the same but also link it twice i guess okay they're voted for in model vote both refer to pokemon uh prisma relations what is my reference on this this is the id from here do i need to make the field here votes for all right that's four references must only refer to scalar eb's are hard guys fields author user field author id references i'm supposed to put that after is it really that dumb okay now it's mad because both of these are referencing with the id two relation relational data models are hard oh yeah tv or t3 shirts that say dbs are hard i'm very down for this uh many-to-many i guess this makes a little more sense do i have to create a vote link that's really annoying but i totally do why is this so complex databases are hard just add a name to the relations can you just do that how do i name a relation oh it's just name colon that's done votes for ambiguous self-relation both refer i guess i can just have votes what the hell relation name oh i guess i have to do this too that relation votes for what the what the hell what the fields relation on model is missing an opposite uh if i just do this vote for vote against cool whatever it's happy now rad we did it i really know what i did different but we did it and get status uh mpx prisma migrate dev it's gonna be one of those migrations where i have to do some manual changes yep so in the migration [Music] create only this is going to give us an incomplete migration where we have to do a little bit of raw sql shouldn't be too bad thankfully yes uh star pokemon regulations cool alter we want to rename this so yeah i can do that quick uh rename column my sql alter table yeah to alter rename i don't like that rename doesn't or isn't a hot worker keyword rename to voted against id rename to voted for id px prisma my great dev where yeah we're on with that french cool ugh oh i remember this that there's i think you can only do one rename at a time in my sequel ready for this to work what the databay foreign keys cannot be created oh i do i need to like set the stupid thing foreign keys cannot be created my sequel prisma uh let's see why does none of this have like the schema it's all things i'm expected to do myself new problems i've never had before this is fun i wonder if it's just because i did the altering i just drop and redo this work nope whatever we don't need to keep the old votes anyways i'd rather start from scratch ah please does that allow foreign keys in the database schema currently redirects here ah this is the referential integrity thing i had a feeling this was going to bite me every feature is referential integrity let's do it nope why did that not do it oh is it because i already have the migration created can i just delete that come on come on found changes that cannot be executed that's fine we can't execute them i'm okay with dropping things and that works cool so oh i forgot to name it oops that was silly of me that's technically not too late to rename it uh add pokemon reference npx prisma migrate reset yes hit status get add migrate add pokemon reference uh ts node that's correct enough cool from here i made the scripts directory uh fill db dot ts we want to have this api from pokey node uh const do backfill equals async do backfill const pokey api equals new const all pokemon equals okey api.list pokemons zero we just want 483 pokemon all pokemon and we want prisma so prisma with this auto import i wonder nope import from back end creation equals prisma dot pokemon dot create many data oh pokemon.map p p name p dot name that's not oh because i didn't wait cool are you me so for anybody who's watching and just saw what happened here that's so annoying this guy doesn't return the pokemon it returns the urls to each of the pokemon named api resource ah does it at least also sorry brent is taking longer than i expected do these have like the name of the pokemon okay cool we get the names from this and the images are pretty easy to auto generate the like url for i think yeah just the number cool we can do that then that's pokemon by name equals all pokemon dot formatted pokemon equals a pokemon dot results dot map p name p dot name p as name string dot name url index is index plus one uh what did i name this in db prisma schema i named it sprite url so that's why we'll name it here id is index plus one and now data is that cool if i did this correctly this should fetch the first 493 pokemon from pokemon db and uh make all of them entries in the database uh npm run ts node scripts fill dbts i want failed uh i can't do the weird pathing because i don't have this included uh yeah i usually just get lazy with this when i have this problem and do source yay networked our npx prisma studio and we have 493 pokemon all named correctly in order and all these should work nice we did it read it i also love that the type i was getting here was just incorrect i'm happy i could fix that that easily yeah cool now that that is fixed i'm going to delete this from here and instead of resolving this from here we're going to const pokemon equals prisma.pokemon.findfirst where id input.id oh this needs to be awaited if uh throw trpc trying to remember like the right i'm just gonna be lazy and throw new error lol doesn't exist instead of sprites sprite url this is going to be on that sprite url we can just return pokemon here honestly that way if we make db changes in the future we're good and here we now type errors because this doesn't exist anymore it's just sprite url now by localhost 3000. oh because i haven't uh reloaded prisma since i made the schema changes that makes sense and that's way faster because it's not fetching from their api every time it's fetching from our model as such it is time to add all of this i should check to make sure voting is working still uh shuffits rounder error cool voted for voted against uh invalid oh because that needs to be id voted for good thing i checked that i'm still going to commit this git commit starting move to internal data oh thanks for coming by graham good to see you man i really appreciate it i got the sprite url now the problem was the persist so if i go back to router in here i don't want to just blindly drop the input in here anymore voted against id is input.voted against voted for id input dot voted for now i've made that change oh this is a tough one leading spotlight cool uh local hosts five five five let me close it i did not i have pokemon here and these all link through correctly we did it we did it read it it's here yeah pretty pump this works get status get add p and get commit fix persistence i still have to migrate the db on prod so i'm going to go do that on the planetscale site sign back in to personal branches figo dev create play request looks good to me i'm gonna have to rerun the uh script that filled it all add changes to deploy queue close a bunch of tabs like way too many open deploy request still going should be ready in a second and yay deployed which means and wow i got the same pokemon twice what are the chances ooh this is a tough one lean in a girl cool this seems to be working now good stuff garante altered interesting now i'm a little concerned that we might have gotten things we didn't intend if that's how garantia comes through whatever we can work with this i'm happy enough from here let's make the view page and after this i'll be done so we want a new page source pages new uh results.tsx export default results page our function results page and div results close the results cool now i'm going to do this a little different because i want to make this page static-ish where it will generate the first time somebody goes to it in a five-minute window so if i go to it and nobody's one in a while it'll generate a new page and then if you go to it within five minutes you get the one i already generated so we don't have to read the entire db over and over again because i expect this will be a kind of heavy query so export get server side props i'm gonna find an example in one of my code bases get server side oh cool you just import this i might even copy and paste this whole guy uh results page make this div results div again kill that and that but these aren't the props that we want we want to do some fetching stuff i might be incredibly lazy and just do this here yeah const result or uh pokemon ordered by or yeah pokemon ordered equals prisma or oh wait prisma was auto import it will not really annoyed by that should figure out a fix for that later from at back end utils prisma and now we have this prisma dot pokemon dot find many uh order all results prisma what i want to do now is some more complex filtering sorting ordering and aggregation stuff so let's okay sorting okay cool uh select what are the options from here okay select where our id i think just like select all basically prisma select all from was gonna do prisma find many it's been a while since i just wanted to grab literally everything without relations just like i want all of this find many required oh this er where is not required so if i just do this console.log did i find pokemon pokemon ordered cool that works so order by voted voted oh let's okay order bye example aha will this just work and i also want to select the count who am i kidding select id true name true url true what else did i want in here uh voted for can i do a count vote voted before i voted against count relation prisma aha out voted for true no where the hell do i put this what can i count in here i have to aggregate to get these things calculations include count oh count select interesting did not know you could select off count voted for true vote against true and this should give me exactly what i'm looking for now i refresh check here count vote for vote against yep i go all the way to the top we should have things i did vote for we do look at that we're now getting the data we expect and this guy needs i'm just gonna const results page react.fc pokemon equals props the type of this is going to be a little annoying i have options for how to get that uh i know what i can do const generate or get pokemon in order equals async return uh why did those brackets get deleted don't want to do that off of trpc uh i had a good inference pattern before infer type script async function a cat i'll find a cat in a second i want to get this working uh i did this before and there's like a lot of ways to do this all of which are dumb what i'm trying to do is infer the type def off of this call which shouldn't be this hard but is for dumb reasons why is this so dumb never all right just to get that to fix itself ooh i have a way to do this i built an inference for infer async return type tsps.ts export that's being used oh type of cool export default results page so what this now does is it infers the type of whatever we do here and passes it through here this is why i like trpc don't do any of this but right now we kind of have to because there's a lot of things we do and don't know that i have to like fulfill that knowledge between because we don't technically know what static props is passing back or is in static this is get server side props and i uh next up js server side props there's a thing you can hand this uh uh revalidate like get static props to revalidate okay here's the incremental static regeneration they do this okay they do it in static okay cool so we do want get static static props and we want to revalidate every 60 seconds we'll say at most once every second seconds cool i like that that's not too hard now this page will regenerate every 60 seconds or when somebody requests depending on how often requests occur and we can start listing results so const pokemon listing equals pokemon gotta make this that number for those that don't know when you have an array type and type script you can pass it number in brackets like this and get the type of whatever it like has on it if the types of the array are consistent so in this case it's consistently this typing for the whole array so if i do this it means we want the type of any one value from it and div or i should source the correct image with next image so let's do that quick it's annoying but whatever div class name equals flex image close others indexed yes where is image uh let's just go ahead and do react nfc this is props last name equals flex flex call if i should make this an h1 h2 is fine props.pokemon.map current pokemon turn tada and now all the results we'll probably do some level of virtualization to make this a performant page but for now yolo uh need to make this page less awful let's not make these 256 let's make these 32. uh flex border bot or border b just to like have a line between each one p four something 32 is a little rough 64. p2 item center next let's call item center last name equals font all right text 2xl i'm trying to figure how i want to handle the max width of this max w 2xl works for me uh yeah i'll give this another container as annoying as it is does make this better now i can set max width here instead uh no i can't actually i guess if i set w full max what that'll be fine it will not be oh because i'm doing item center i can kill that then it will fall with there we go nice and border okay two results i'm just going to pee for that it has padding on the top as well right we now have a results page that works get let us get add results page ta-da i can add some like more info here if class name equals flex this is just so i can keep the name and the sprite close to each other because i want to have another section here that's like the count uh how do i want to generate this also getting annoyed doing props up pokemon for all this so pokemon kill both of those pokemon votes that's that account let's generate count percent equals burn divided by vote for vote against equals pokemon [Music] count we want this to be vote for divided by vote for plus vote against all of this times 100. between nice fo4 plus the against equal zero return zero that's just like the escape for if there's no votes on a pokemon yet cool i think that this is all mostly done now like a little bit of padding on the right there class name equals pr one that looks about right and status get add p add percent round count to results page cool now of the things i still wanted to do how are we doing we decided against that one we did this we did this better loading state between votes last thing i wanted to do add this readme change quick let's first see if the deployment hit roundist.t3.gg i should add a link to the results let's do that quick too for link from next link interf equals results good enough i vote for shroomish go back extremist is in here now yay nice i owe a cat i need to go find that quick let me just push this link to your results page i hope that wasn't streaming the screen god that's so sad i just had a really dumb mistake that typescript saved me on so i'm gonna recreate what i just did i abstracted these loading checks and i did this not is loading and i got the error here type undefined is not assignable to pokemon it's like okay that means this type inference is failing i'm just gonna not those so that i get my pass i did this i went here and i type errored it's like okay what did i do wrong i looked through and i realized oh this isn't the loading state this is the data loaded state and what's funny here is when i delete these is smart enough to know that a condition underneath this binding we made here is safe on this so if i delete this one line this will type error because the type inference from this constant is strong enough to correctly infer within the jsx subconditions that is madness typescript is so good and if you're not using it already please tell me why so we can fix that because man even like junior like the beginning of your career developers this is easier to do so now that we have that not data loaded and i want an svg for this so svg loader svg oh awesome spinners this is the one i was thinking of somebody made this collection of svg loading spinners i there's a really good is it loading i o no it wasn't is the svg loaders yeah this is the one i was thinking of cool i love this one i've used it forever save images cool rings that svg uh can i scroll up to public rings svg not the most compressed thing i don't really care image source equals rings.svg uh the const data loaded equals false so temporarily oh it says don't need to do public just slash rings so the annoying thing is going to be like the ui shift i almost want to move this out of the container here this was just a justify between and remove these guys from the footer yeah i like that and then pt eight give it enough space oh i don't need the padding too there anymore that's what's causing that weird problem all right there i probably need the one here still okay cool that's what i was expecting that to do uh class name equals with 48. cool so it's really fast here but it will not be that fast on the server i think there's a better way to do this actually now it will never ever hide it shouldn't the data should always exist oh no it won't because we're doing a new query yeah yeah there's a whole new query instance so caching the old data is a bad call here yeah i will leave those loading states then i do think this is as good as that can get then good to know yeah this is fine this is totally fine yeah this is so much better than it was and it is a really cool demo of all the things i wanted to show off so it add decent enough loading state cool i guess that we can call that complete so how's that for time three hours 10 minutes not counting all the time i wasted talking to y'all spitting up random other things dealing with my cats etc to build a fully functioning scalable production service that solves my long-standing question of which pokemon is roundest also i'm noticing we haven't had the deploy with results yet is there something going up with the deploys cool we're getting errors in the deploys what's up does not exist oh i know what i'm doing wrong i'm stupid uh i'm gonna go back to this repo because we need a post install for this to work on the server and post install for prisma cool and let's make sure this builds and then i will call it a night new build kicked off here we go we're off for the running are we doing good that's so cool it generates initially there so it's going to output a static html page and read it regenerate it every 60 seconds and even calls that out here that's so cool that's literally one variable and it just does that next.js is magic the versailles is kicking so much fast cool uh that means we now have round is t3 ggg with the results always initial load failing responded with oh i never ran the db migration on production that's what i forgot to do not the migration the uh and not the odell i want to do main p scale connect main so i have to run that script again there we go and now this works cool that was so easy we did it howdy friggin mudkip good uh good person to stop by at this moment because now oh i'd cacnea not load oh it did it just took a while center's pretty round knocktowel's pretty round palki is pretty round squirtle's pretty round i'm gonna share the link in chat for everybody who wants it because we now have a real production service and a results page that yep now it has regenerated oh we're currently doing that oh we're not sorting by the right value right now we're going to see people with higher percents than others how do i want to handle that i have a quick fix this is dumb but it works and since it's like server side i don't care as much perhaps a pokemon.filter or not filter sort a b generate b minus generate count a cool this will now sort by percent instead of by vote count which is good enough for me i vote count instead by percent instead of vote count i'll go make it to fixed too fine fixed digits are fixed decimals on percents get pushed cool i'm calling this done momentarily this service will get updated such that the 33.33s and 66.66s will no longer be that way and will instead be formatted more correctly i'm expecting the results on this to be i want to say lackluster but poorly representative of the true roundest pokemon until we've had this going for a while but over time i would expect these numbers to become more meaningful it looks like the seconds deployment went any moment now there will be another deployment where all the hundreds will become 100.00 and mu will be 66.66 oh wait no they're in the wrong order again everybody's getting a different cached asset that's pretty cool that that can be a thing since i'm caching these every minute well i think i can say safely that we did it really appreciate everybody who came by for this one i know this was a goofy or an extra goofy stream but i hope you got to learn a few things from it please feel free to reference this stream share with others ask questions in the future whatever else i wanted this to be a an example of how all of the parts i like to play with come together when you have a problem you're working backwards from and i think this is pretty close to what i was expecting to build when we look at here and here i would say this is almost exactly what we had sketched out so yeah oh and there's the 100.00 because we did the fixed so this will now look much better too
Info
Channel: Theo Browne
Views: 834
Rating: undefined out of 5
Keywords:
Id: PKy2lYEnhgs
Channel Id: undefined
Length: 169min 9sec (10149 seconds)
Published: Tue Nov 30 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.