Building a Backend for React with Next.js, Prisma 2, and Postgres

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey how's it going everyone its Lee holiday and in this video we are adding a back end to this beautiful map we've built in a past video the back and we'll be building will live within an XJS application and we will use Prisma to connect to our Postgres database so if we take a look at this beautiful bear sighting map it allows us to when we spot a bear click and add those locations to the map you can open them and see sort of when this bear was spotted and warn your fellow humans in the wilderness that there's danger nearby but we had a major I guess problem you could say because as soon as you refresh the page the bear sightings are gone and your friends have some real problems on their hands out there in the forest so the reason that happened is because there was no back end we were just storing all of these bear sightings in state so in memory within the browser as soon as you refresh it's gone so we need to actually store these in some sort of persistent database so when we think about the back end we have a number of I guess choices to make just like when you're doing a front end you could do it in view angular react obviously like we're doing here you can do typescript or JavaScript and there's where are you gonna store your state and redux and mob Xing anyways there's a tons of questions on the front end and you have probably just as many questions on the back end so this is this little chart I put together so you're working on the back end which i think is awesome because it's great to strive to be a full stack developer so that when you build this beautiful front-end you can hook it up to something on the back end to persist that data to send emails to to do all sorts of stuff that you can't really do right in the browser very easily so you pick your language you can do it in a node like we're gonna do today but you can use sort of any language on the back end because this is code that runs on somebody else's server not on your users browser like like you're dealing with in the front end so you could do Ruby on Rails you could do laravel you could do Django that's a joke I don't think you can do Koval huh maybe you can I think a lot of the government still runs on Koval but once you pick a language you have to sort of pick a framework for that language to run in so we're going to be working in next j/s because it's not just a front-end framework you I guess you could say it's full stack it allows you to add api pages or api routes but you could do any sort of number of other frameworks that you want to run on the backend like nest redwood express adonis etc once you pick your framework you have to basically decide how the front end is going to communicate with that back-end what sort of layer API layer in between are they going to use so you've got your sort of traditional rest which is where it goes based on the the path of the URL we'll look at this in a little bit and typically JSON responses are being sent back and forth you could also use graph QL and you could use soap that's another joke probably want to avoid this I've had some nightmares over the years dealing with that so once you know how the front-end is going to communicate to the backend you need to basically choose where your data is going to live so in your database and you can choose sort of different types of databases to store your data in a relational style database so there's post-grad as MySQL another joke here Microsoft Access fun fact Microsoft Access was the first database I ever used about 15 plus years ago with Visual Basic 6 it was pretty awesome back in the day there's document style databases like or dynamo and there's graph neo4j DeGraff etc so we are going to be using Postgres database on Heroku because Heroku has a free plan which is pretty cool next how are you going to communicate from your JavaScript code to your database you need some sort of I call it o RM but some sort of query builder some wait for your JavaScript code to communicate with that database um we're going to be using Prisma it's not exactly an or em it's more like a query builder but it's pretty cool it allows you to sort of delete data create data read data etc finally when it's ready and we're not going to touch on it in this video just because it's it's gonna be pretty long as it is I'll do a follow up if people are interested you need to deploy your code sort of take it off of your computer running locally and put it out on the internet verse L is really awesome for next Jessup's Heroku is great too that's where we're using a database so yeah let me know if you're interested in that video and then we add some more complicated back-end topics like authentication scheduled jobs background jobs error reporting we're not gonna have time to get into that in this video but this should give us lots to work with so this next j/s application that we've got it's got the map setup and it's time to start adding in those elements to build out a back-end so the first thing we're going to do is setup Prisma and pers maas as i mentioned is the way that your back-end code can basically connect to your database so what we'll do is we'll go down to the command line and we will stop our out from running I'll just clear that out to make it nice and fresh and then the first thing we want to do is add this Prisma CLI and what this will allow us to do it's a dev dependency we don't need it when we actually deploy our code but it will allow us to run Prisma commands to basically generate files and run migrations etc so we'll add Prisma CLI and then as soon as that's done we will add in the Prisma client Prisma client and that one is not the dev one it's the normal set of dependencies and with these two in place we can now run a command so you can run this a couple ways you can do yarn run Prisma works and you would run in it you could also do npx Prisma in it but running this command what this does is it sets up your it creates a Prisma folder and it sets up some initial things needed to connect to your database so we'll pop back open to the app let's close out everything so we've got a fresh start and right here we've got this Prisma folder so it gives us a couple things the first thing it gives us isn't a dot end file you should not commit this this is where you're gonna put your secrets your environment variables as you're developing locally but in here the key thing is you need to tell it how to connect to the Postgres database or MySQL or whatever database you're using prism awards Postgres MySQL SQL Lite you need to put in your database connection URL so at this point why don't we just pause and we'll hop over to Heroku and we'll spin up a free Postgres database so come back to the browser so I already set up a free sort of throwaway account because just for the demo so this thing doesn't have any servers at all it's basically just empty no add-ons no servers running they call them dinos but what we can do is click over to resources search for postgrads and we can just add a free hobby database it allows you to store I think something like 10,000 records for free and if you need more you can add them so we could have set up a local Postgres database using like the Postgres app on our Mac or something like that but this works well because if we were to deploy our code somewhere this database is already out on the Internet and we wouldn't have to do anything else so if we hop over to settings and we reveal these convene bars this gives us that database URL that we need so we'll come back and basically just paste this in and now Prisma knows how to connect to our database another file Prisma gives us is this schema dot Prisma file and this is where you basically define a lot defines for you how to connect your database what provider what type a database is it and this is where we'll define the tables or in prism language they call them models where our data will be stored so that is the next step what we'll be doing is defining our first model so the way it works is you say model and then you give it a name and then inside you define what types of columns this table is going to have so model table are sort of synonymous and Prisma vocabulary so we're gonna give our citing table and ID which is an integer and we'll just define a couple things here we'll say that it's an ID and it has a default value of auto-increment so basically we'll start at one and just increment for you as it goes up let's add a created at column of type date/time and we want to give it a default of now so that we don't have to sort of set when this bear sighting has been created it will just automatically set it to whatever now happens to be and we want to add latitude which is a float and longitude which is a float as well this point we've sort of defined our database schema inside of Prisma and the next step is how do we take this schema and basically get this table created inside of our actual database because right now it just lives in this in the schema file so it's not really doing anything so what we'll do is we'll hop down to the command line again and last time I ran it with yarn this time we'll run it with NP X it's running the same thing so we are going to run NP x Prisma migrate safe so if you do this it doesn't let you it tells you you have to add an experimental flag and this is because their migration framework is in beta right now so they want you to to be cautious that it's maybe not ready for primetime but it's pretty cool so add that flag and what this does you have to give it a migration name so we're gonna create a sighting table or model so when you run that this is very cool what it does is it creates you a migrations folder and it basically takes a look at your schema how it was and how it is now and it figures out the difference and it generates sort of the SQL necessary for your database to get into the the state that you want it to be so inside of migrations it gives you this readme file that basically tells you what it's going to be doing it was generated by myself at 7:05 in the morning it this is the SQL that's going to be run and this is the difference between the old schema and what we have right now and then it's got some internal files that it uses to actually run this migration so with this in place we can pop back down to the command line and we can run instead of save we can run up so what up does is it basically runs any migrations that currently haven't been applied to the database it applies those so maybe at this point what would be cool is to actually peek into the database and see if it's created the table for us so I'm gonna come back and I'm gonna copy this same post-grad connection string I'm going to go into a database client that I have the table plus and I'm going to say new connection from URL it think it already pasted in for me and I'm gonna click import so this will be my Prisma demo we'll call it the test make sure it can connect it's green so it's all good so why don't we save that and then connect cool so this is the database it's got two tables migration you don't have to worry about this this is something managed by Prisma itself this is what it uses to know which migrations it's applied or run and which it hasn't but you can see here we have a sighting table so this was created based on the schema definition it's got no data yet but we can click into the structure and we can see that it's got the columns we define the datatypes the the auto increment all of that stuff it adds a primary key index all of that good stuff so this is ready to to be used so what we're going to do now is we're gonna start defining our end points our back-end code so we've got our our Prisma set up it's defined our database tables but now we need to basically if you've done front-end development you've connected and you've you've interacted with different servers via HTTP requests now it's time to actually build that part of it so within next j/s it all works based off of those pages folder where you can define different pages and that's sort of the routing and the URL that it uses so we were looking at the map here which is the index the home page but if you add an API folder within next yes these are basically pages that will only run on the server there's server side they're used for sort of AP building out an API which I guess dumb way of saying this is where you put your your API endpoints so with an API of a folder called sightings and I've already created the two files and sort of another one whenever I want to clear out my my data as I've been playing around with this the first thing one we are going to look at is create so create is obviously where we are going to receive the data for a bear sighting and we're gonna have to save this to the database this is the typical structure of a back-end API route within X GS if you deploy this to ver Sal these all run within serverless functions but they all sort of follow the same format where you have to export a default asynchronous function that receives two things the first is the request this is all the information coming through the HTTP request from the browser or wherever it's coming from and then your response this is what you're going to respond with back from that request so why don't we just take a look first at sort of how the data is arriving and how we access it what we're going to be doing is we're just going to console dot log the request and a request contains many pieces of information what the URL is what query parameters are but one of the things it receives is a body just like that and we'll save that so if I hop open to another tool called insomnia so I've already been playing around with it here let me just clear it out okay so insomnia is is an HTTP client for the Mac that you can basically play around with HTTP requests for now we are going to be posting data to our localhost 3000 to a P I sightings create so that was the file that we were looking at make sure that the server is running perfect so if I post data to the backend click send it's never going to finish because I haven't responded yet at all from the server but if we come down here we can see that the body this little empty brackets we receive no information so if we come back here and we send it some information so why don't we send it citing this has to be JSON so citing and we'll send it a latitude of say 20 and a longitude - 20 I don't know where that is in the world um send it again it won't reply but we'll come down and we should now have this information in our body because it was posted to this endpoint as JSON so we come down here and we can see that we have access to the siding now so now our job is to basically take this siding information and save it to the database using Prisma and then we want to respond to the user that everything was okay and here's the sighting that we just put into the database so the first thing we should do I think is start to build up what the response looks like to the user so the way you do that is you can use this response object and we can give it some JSON so what we can do here is we can say citing saved eventually we'll respond with the actual sighting but just by adding this little line here if we come back to insomnia we now get a response from the backend so we're sending the data to this endpoint and this is what the backend is responding to us as server responses there's two main things that that they respond with the first is the status that basically tells the browser whether things worked or not whether there is an error etc so by default next J's responds with a 200 which means okay and then the response body so we responded with this JSON here okay we can override the status by calling the status method and we can respond with 201 so 201 means created so since we're creating data that's probably an appropriate one to respond with but now why don't we get into actually saving this sighting to the database so the first thing that we'll do is we will create an instance of the Prisma client that I've already imported here so we'll say Const Prisma is equal to new Prisma client you don't need to tell it how to connect to the database it already knows via the environment variable that it's looking for but a couple things we could do is we could tell it basically how to debug so we're going to debug by putting some information of the logs about our query just like that and that needs to be in an object and I'll help us figure out what the heck prism is actually doing so now that we have a connection to Prisma we are going to wrap everything in a try-catch block which has a finally so what is finally finally happens when tri is done or when ketch is done sort of no matter if it succeeds or fails it will always run this little block of code at the end and will actually fill in this first what we want to do is connect our dis or a disconnect from our database at the end of the request and we want to do this to free up connections to our Postgres database because sometimes there's a limit and once they get used up it will start rejecting new connections as they come in so we can disconnect at the end and we want to do this whether it fails or succeeds and we can also deal with what happens if it fails so maybe we will respond with a status of 500 and we will respond with an air that says sorry JSON that has error sorry unable to save citing to database like that so try is where the actual sort of meet of this happens and we want to take the data that came in in the body of the request and save that into the database so the first thing we can do is we can grab the data itself so that came from the body and it came in a sighting like that and why don't we actually name the variable sighting data rather than sighting so with the sighting data we can now take that data and insert it into Prisma so we will do const sighting equals a wait and we'll use Prisma dot sighting dot crate and when you create it you pass in the data that you want to create so we want to save the latitude and the longitude so latitude comes from citing data latitude and longitude comes from citing data longitude just like that so if I save this it receives the data from the body it inserts it in the database as a response we should get our citing back so why don't we now respond basically what we are doing down here which was just for demo purposes so let's delete that and we'll come back here and we'll say the response is going to be a status of 201 which means created and it will respond with JSON with the new citing just like that so if I were to come back to my HTTP client and make the same request now what we get back is the actual sighting that was created in our database you can see that the database automatically gave it an ID of 1 it automatically set when it was created and it's got the latitude and longitude so if we come over to our table plus and we refresh this we can also see that in the database exists this latitude longitude we created so why don't we try to send up a null value because when I define the database I said this column has to have a value so if we send this now we get our error back sorry unable to save citing to the database so we're handling errors okay to save some some bad data from getting in and we're saving the good data so we'll just add like I'm 25 I have no clue where that is in the world but so that's saving that here so the next end point we want to worry about we've saved the data why don't we build the index which allows the user to sort of fetch all of the data we'll do this one a little bit more quickly since it's very similar to the one we just did so we want to try catch and finally and finally this word will disconnect what are we disconnecting from our Prisma database so that is equal to new Prisma client we'll do some logging like we did last time query level so what we can do here is we can try to find the sightings so we'll say a Const sightings is equal to a weight Prisma dot sighting and then they've got something called find many which allows you to query from the database the sightings and once we have these that's what we can respond with as our JSON response so response dot JSON and we'll set it to I'm just trying to remember how I want this to arrive to the client we will do it like sightings will we'll send it down like this so you don't have to say that the status is 200 but you can if you want to be explicit we should handle errors so a status of 500 give the user a friendly message error unable to fetch sightings and then finally we need to remember to disconnect from our database so a weight Prisma dot disconnect ah so with our index in place which is fetching the settings from the database and returning them as the response we could go into the browser and we could open up localhost API sightings and it will query the sightings and it will return them as an array which event this is what we're going to be showing within our react app so with create in place with our index our or read in place which illuminate as a get request we're finally ready to hop back over to react land and we're going to use react query to to read these and to write these back when the user clicks on the map and this will sort of go full circle and allow us to persist the bear sightings so that we can help protect people out in the wilderness so we're basically done with the backend we're going to hop over to react land again and our goal is basically to replace this state here of sightings instead of reading this from memory we are going to be fetching it from our server and when you click on the map and it was adding a new sighting to our state instead of doing this we instead want to send a post request up to the server to save this information in our database using Prisma so why don't we get the read working first and we'll just actually comment out map clicks for now and so we're going to be deleting this state and replacing it with a request to the server using react query I'm gonna pop this out so react query I have another video which covers some react query in more detail so we'll go over this fairly quickly and I'll link to that video below so you can access it so from react query I have already imported the three things we're going to be using use query you use this for making get requests for for reading data so that will be our sightings endpoint to get a list of all the sightings you mutation you use this whenever you're changing or mutating data on your server so in our case we were posting data to for a new sighting so that's we'll be using in query cache will be messing with this a little bit to have optimistic UI updates so that when you add the bare marker it appears instantly while in the background it's making the post request to the server so that's what we're using so we'll come down here we'll say use query you have to give it a key this is what it uses to keep track of some internal cache of sort of what sightings are currently stored inside of its memory so sightings and you have to give it a function to basically go and make that fetch request to fetch the sightings from the backend so we will create a function and what should we call this we will call this fetch sightings request like that so we'll define this in a second but what it gives us back is the data which would be the sightings and it gives us some other information about an error and about a status loading styles etc we're gonna yellow it and just assume that things are working you only live once right I know really you should deal with that but for the sake of time we're just going to do this so we're gonna define our async function fetch sightings request up here and what it will be doing is making a fetch request so const response is a weight fetch where is it fetching data from it's fetching from API sightings just like that and we'll take our response and we will convert that to the data so you will await the response JSON and our server responded with data that looked like this sightings and it had an array of sightings we want to basically grab the sightings and respond so we'll say Const sightings is equal to data and then we'll just return our sightings so with this in place and react are sort of used query from react query calling our fetcher function saving the data into its cache here and returning it here let's see if this works so we'll pop open the map again refresh the page and I saved it to 20 minus 20 which is way over here close to Africa so it's loading our bear sightings from the back and from this end point here and it's now displaying them on our react map rather than reading them just from state so we're sort of halfway there on the front end the next thing that we need to implement is how when the user clicks to make that post request back and save those in the database that's what we're going to be working on right now so to do that we are going to create a custom hook and what our hook will respond to us is an array where the first thing in the array is a function we can call to trigger the mutation to trigger the post request so we'll call this create sighting and we'll call the function we are going to create the hook use create sighting like that so this doesn't exist yet but we can already start to implement what happens when you click on the map so when you click on the map rather than adding the sighting to state what we want to do is call create sighting and we want to pass up the new sighting so we will pass out the latitude which comes from the event I have a right here so I'm sort of cheating event dot whom lat/long lat like that and longitude is e dot lat/long dot lng just like that so with this in place I can delete this code and it will call create sighting pass up the latitude and longitude and it's only up to us now to go and implement this custom hook so what will this hook do this hook use create use create sighting it's basically just going to be a wrapper around the use mutation hook from from react query so use mutation first thing it wants to receive is what function to call to actually go and make that fetch request back to the server so this is something that we need to define so we're going to come up here and define this so async and we will call this create pacing function create sighting request I actually remember how to do a post request with fetch problem maybe not so what does this receive it's going to receive sighting data it's basically whatever actually not as an object as normal whatever is passed to create sighting is going to be passed up to this function to actually make the request so just like up here we want to say we're going to wait fetch to send the data to API sightings create but we have to send in a bunch of options to override it from making a a get request which it does by default so we need to tell it to use a method of post we need to tell it that we're sending the data up as JSON there's many ways you can post data to the backend so this is defined inside of the headers that are sent up well what is this called again content type content type is application JSON and we need to tell it what the body of the data is that we're sending up because remember back here on the backend we grabbed the sighting from our request body so now we're implementing the front end of that where we are taking this sighting data which is this stuff right here and we are sending this up but we have to take this object and Jason dot stringify it so stringify the sighting data okay with that done what we can do is we can convert this into the data so a weight response dot JSON and then we can grab the sighting that was created like that and then we can return it okay so this creates sighting request is ready and now we can pass that function to use mutation from react query which it will call whenever we call create sighting so I mentioned we wanted to do optimistic UI responses first things first why don't we just check to see if this thing is actually working because I think at this point it actually would work so you may have been wondering why when I call my use create sighting function or hook do I have an array coming back where the first thing in here is this function I can call to trigger the mutation that's because that's what use mutation responds with if you look at their documentation it often looks like this mutate is equal to this so what this is responding is an array where the first thing in the array is a function called mutate but you can call it whatever you want which triggers off a request to whatever function you passed here to make the actual sort of fetch or HTTP request so what I've done instead as I've just returned this which is why I have access to this array here and instead of calling it generically mutate I called it create sighting oh boy Sun is in the eye all right anyways let's see if this works so refresh the page click click so nothing's happening yet but if I were to refresh nothing still happens so it's time to troubleshoot what is going on so when I click here internal server error so that's no good let's go down here and see what's happening hmmm it's not telling us any information this is actually good I have no clue what's wrong so why don't we come back here and why don't we try to catch and console dot error the error so maybe we can see why this is not working so sightings create that seems right so let's do that again come down here cannot read property latitude of undefined okay so that's the issue we have and I actually do know why that's happening now that I see the actual error the reason is because when we sent the data up to the server the server is expecting it to app to arrive sort of inside of an object that has a sighting property but we didn't send it up we just sort of said blah here's the latitude and longitude as the root level of the object so we need to come down here and instead of string a find it like this we need to stringify sighting like that so now if I were to refresh the page let's let's add some bears near Guelph we can come down here to network and we can see it's making requests it's responding correctly it's not displaying it on the UI yet that's what we're gonna do right now but if I were to refresh the page the Bears are there they are being saved at the database pop over to the database refresh here's all of our bear sightings being added to the database now we are going to last step is basically add in some customization to use mutation to optimistically refresh the UI as soon as a bear is added so as soon as the user clicks the bear is going to appear rather than having to refresh the page or something like that so the way we do this is we can tie in to use mutation to a few different sort of callbacks I guess you could say the first one is on mutate and this is a function that when it's called basically whenever the mutation is kicked off so whenever we call create sighting it's going to send that and call this on mutate call back and what we're going to receive is the sighting data and we'll fill in the the body of that function in this ech a next one we're going to do is on error so this is what will be called sort of if the mutation fails so here we're given a few different pieces of information info but the error the sighting data that was passed to the mutation and whatever is returned from this on mutate callback function here so we are going to be returning a function that can be called called rollback to basically revert the optimistic UI into whatever state it was before so if there's an error we are going to call the rollback last thing we are going to hook into is unsettled this is a function that is called basically when the mutations all finished up successfully and what we can do actually settled I think that success or failure but I'm not positive about that you can check out their Doc's if here if you're worried so what are we gonna do on settled we are basically going to tell react query to just go refresh the latest data from the server so the way we're going to do that is we're going to access the query cache which is the third thing I imported from react query and we'll tell it to invalidate queries believe it's called and which query do we want it to invalidate we want it to invalidate this sightings one so that will basically tell it to go and refresh the data so the meet of the work we need to do is inside of on mutate and we need to do four steps so the first thing we need to do number one is we need to cancel any queries that are currently being performed so that this doesn't mess with our optimistic UI refresh so cancel queries number two is we want to save a snapshot of whatever is in the query cache that will be used to rollback if there's an error number three is we want to optimistically update cache so that even though the back-end request has not finished the HTTP request we can at least show the user what they've clicked and they've added a bear sighting the last thing is we need to return the rollback function which resets cache back to snapshot that's a mouthful so one thing at a time let's work on canceling queries so we cancel queries by saying query cache dot what is it called cancel queries we want to cancel any queries on the sightings cache key next thing we want to do is save a snapshot so basically figure out what the state looks like right now within the query cache so we'll call the snapshot it will access the query cache and we'll tell it to get query data for sightings like that so optimistically update the cache so even though the server has not finished why don't we take this sighting data and insert it sort of temporarily until it invalidates and refreshes the queries here below and that will give instant feedback to the user so what we'll do is we'll access the query cache again and we will call set query data we're setting it for the sightings key and we can pass in a function and our job is basically to return what the state should look like but we are given the current state brief we could have also used snapshot here since we just grabbed it but we don't need to so what we're going to do is we're going to take the previa and also tack on the information about the sighting that the user just wanted to add so our sighting expects to have a couple things it expects to have an ID because it's not in the database yet we need to create a bogus one for a few seconds so we'll just say a new date - ISO string so that we have something sort of unique it wants to have a created at so literally the exact same thing - ISO string and then we'll spread in the sighting data cool so we're optimistic dating our cash with our sighting data last thing we needed to do is return a function which will be used as rollback if it fails and what we wanted to do here is basically take our query cache set the query data for sightings back to the snapshot so that is why we save the snapshot prior to changing the cache right here okay so that was a lot of coding how do we see if it actually worked so I refresh the page I click somewhere in the forest and instantly I get feedback that that the user clicked and this is the data that's been added optimistically that's not necessarily what is in the database that's the optimistic one but when it is finished it will just go and refresh the date the latest data from the database so what you should expect to see if we sort of dive into the network tab is when you click this it's going to post the data to the server and then it's going to refresh a get request the data from it so we'll click on the map so I guess it does one prior to kicking off the mutation it does one to post the data and then it does up I know why I did this one prior react query has this thing in place for when the window receives focus it should reach the data so as I'm going from the network tools into the map it views that as window sort of has focus again that's what this one is this is my actual mutation and then this is the unsettled going andrey fetching the latest data from the server so this was a big video we're done but we accomplished a heck of a lot of code what we did is we used Prisma to basically define our database schema what data we want to save and in what format here in our model we're connecting to our database through this environment variable which is a Postgres database on Heroku we created two endpoints one to create the data that we're receiving and save it into the database another one to read all of these sightings from the database using Prisma and then we went in and we implemented reading and writing data to our back-end and eventually the database using react query so we created two sort of functions use query fetch sightings request which we're all the way up here which reads the data using fetch from our back-end and then we created this um use create sighting sort of custom hook which basically hooks in to use mutation it's doing a lot you may think oh is this even worth it but you're optimistically updating the UI to give the user instant feedback so I think it's worth the effort for the great user experience and when this mutation is called via create sighting when the user clicks on the map it makes this post request to the backend which I almost got right first time it's hard to remember all this junk here but it posts the data to here with these headers so that the backend knows it's receiving it as JSON and we passed up the body which is the new sighting data we wanted to add so at the end of the day we've accomplished going from only using state and react to building out a back-end using Prisma postgrads saving our data so that when the user refresh of the page the bear sightings are still there and we've saved our friends in the wilderness hope you enjoyed this video let me know if you'd like to see me deploy this app to her cell so you could sort of make it live to the world if you loved this and you're still hanging around please subscribe I'd love to have you take care bye
Info
Channel: Leigh Halliday
Views: 19,466
Rating: undefined out of 5
Keywords: fullstack tutorial, backend tutorial, prisma, postgres, next.js, prisma tutorial, prisma migrations, next.js tutorial, prisma postgres, react-query, optimistic ui, react google maps, react tutorial
Id: Bqacj0iOL68
Channel Id: undefined
Length: 49min 51sec (2991 seconds)
Published: Wed Jun 24 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.