Client-Side GraphQL with React, v2

Refreshing Issues & Optimistic Updates

Scott Moss

Scott Moss

Superfilter AI
Client-Side GraphQL with React, v2

Check out a free preview of the full Client-Side GraphQL with React, v2 course

The "Refreshing Issues & Optimistic Updates" Lesson is part of the full, Client-Side GraphQL with React, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Scott discusses different solutions for updating data in a to-do list without refreshing the page. He explores options such as rerunning the query, using real-time updates, and implementing optimistic updates. Scott also briefly explains the purpose of the .db and .migrations folders in the project, which are related to the database schema and migrations.


Transcript from the "Refreshing Issues & Optimistic Updates" Lesson

>> Scott: Obviously, that's not a good experience. So outside of GraphQL, what are the solutions? If you were to do this with REST, how would you solve this? I did a post request, I created a new to-do item, I needed to show up on my to-do list, how would you solve this?

>> Scott: Anybody wanna tell me how they would solve it? No wrong answers.
>> Scott: No, okay, I know y'all know. All y'all are smart. I have a to-do list, I made a post request to go creator to-do, but now I want that new to-do to show up on my list immediately without me having to refresh the page.

How would I solve that issue if I'm using REST, yes?
>> Speaker 1: Rerun the issue query.
>> Scott: Yeah, you would just say, cool, just rerun that get request that got all the to-dos again. Because this time, it should be whatever it was plus the one I just created, so you'll just rerun it, right?

That's something you could do. You might be, I don't know, maybe this is a feed, maybe this is not a list, but it's a feed, maybe there's someone else, there's real time elements. So maybe you're polling, so on some interval, it's checking for new things. And then eventually, it will show up.

That's another solution depending on your UX. If you were using something like Redux, after you created that new one, you would get back the new one from the mutation. You go into Redux and insert it exactly where it needs to be in the array of to-dos. So, and then Redux would trigger a re-render and then it would just show up on a page without a network request.

That is something you can also do with Urql and it's probably a recommended approach. But it's very specific, [LAUGH] it's quite complicated. It seems simple, I'm sure they built it in a simple way, but it's still quite complicated in my opinion, but that's typically how you would do it.

What we're gonna do is we're just gonna refetch it. We're just gonna just refetch the whole query to be like, hey, you got some new stuff, your data is stale, I need you to refetch. So let's go do that. And the best way to do that is to figure out where we need to do it.

So in this example, onCreate, where we need to refetch, would be here after we know that there's data. And right before we close the model, we wanna do a refetch here, right? So how do we refetch their original query? Is there a function we can use from the original query that allows us to refetch it?

>> Scott: Anybody wanna guess? Right here, replay, this function right here. This function right here, that's what it's here for. This function allows it, if you just call it, it'll rerun this whole query again. And this query is responsible for getting the issues. So this should work. So let's try that.

So I'm gonna say, cool, go here, await replay. So let's try that. I'm gonna create an issue.
>> Scott: Do that, and that, boom, now it shows up,
>> Scott: Right?
>> Scott: Right, it goes through all the states, it goes through the loading again, checks for error again, it goes through all of that.

So that's one way you can do it. Is it the most optimal way? Probably not. The most optimal way would be using an optimistic update, which if you know what that is, it's basically updating your local cache to what you think or know the server is gonna come back with.

So in this case, we would actually know what the server would come back with if it was successful. We know everything minus the ID, cuz we don't generate the ID, but we know what the name of the issue is cuz we sent it up. We know the content of the issue because we sent it up.

We know the status of the issue because we know it defaults to a certain status. So we don't even have to wait for the server to come back, you can put that on the screen. Just go ahead and make a fake issue with that same name, that same content, that same status, the ID doesn't really matter.

And then put it in the cache so it shows up on the screen immediately. And then when the new one comes back, cuz it's still gonna come back, or if we refresh it, what we'll do is we'll go and we'll basically do a diff on the one that we created, the fake one, and the real one.

And whatever is different, the real one will override it. In this case, only that would be different would be the ID. Everything else will stay the same, so therefore, you won't even see anything change on the screen cuz nothing really got changed that's visible. But the bad thing about that is if you optimistically put that on the screen and then there's server errors, now you gotta take it off.

And that's a really shitty experience to be like, I showed you this. That's like you at your bank and you go initiate a transfer, and the money leaves your account, but then I guess something happens, so then it immediately reverted back. You'd be very confused, what actually happened here, cuz I saw the money leave my account, but now it came back.

So did it send it or did it not send it? So it's really not a good experience. So optimistic updates are like, you have the utmost level of confidence, [LAUGH] and everything, and you just know that things are gonna work out. And also, I guess I don't really like optimistic updates that much, because even though the thing is loading, depending on how you load it, it could be blocking rendering.

So even though the thing just popped up as loading in the background, that could prevent some interactions on the page, and the user wouldn't know that because there's no loading indicator. You just threw something on a screen and it's loading in the background. So how would the user know something is loading?

So it can get pretty sloppy with optimistic updates. If used right and used well, I think they're the best, but you have to invest in them. So I think the next intermediate step is just wait for the server to come back. I'm sorry, the next immediate step, yeah, wait for the server to come back and then write that result to the cache.

And then the third best is what we did, which is just refresh the whole query, just go get the whole query all over again, which is fine in this example. But if you have something like pagination or something like that, you couldn't refetch, or you wouldn't wanna refetch all the pages just to get one new thing, that would be wasteful.

So you probably do wanna write to the cache and come back, so yeah.
>> Speaker 1: Can you explain a little more about the .db/ and ./migrations folder?
>> Scott: The .db and .migrations folder. What are we talking about? Are we talking about, this right, the db folder and? Okay, I mean, yeah, I could talk a little bit about this.

So the db folder, I want to make sure I'm not missing anything that they were talking about.
>> Speaker 1: And I think that migrations folder, that's about middle of-
>> Scott: There we go. Okay, I see, I see what they're saying. Yes, I'll talk about these. So this has nothing to do with GraphQL.

This has nothing to do with Next.js or React. This is just the actual interface in the database that we're using. So we're Turso. The db file is where we create our client that connects to Turso. And then we create our instance of our ORM using Drizzle. There are plenty ORMs out there like Prisma, Drizzle, Kysely, SQL.

There's so many of them, TypeORM, name it. There's so many of them, but we're using Drizzle here. It's my favorite one, it's the one I know the best right now. I also like Prisma. The schema file is literally the database schema, you can take a look at it if you want, it's using Drizzle.

If you know SQL, you'll feel pretty familiar with this, it's nothing crazy. The migrations folder is, so this is where you store the migrations for your database. When you ran that command at the beginning of the course, where you had to do db push, that didn't create a migration.

Actually, I already pre-generated the migration in the repo ahead of time, and this was the migration that got generated. It's a random name, I didn't come up with a name, but it's basically the SQL. When you run a migration on a SQL database, you have to describe all the things, all the commands that you want the SQL database to do.

So you could think of this as a reflection of the schema written here. So this is the schema, you can see I have a table called users that have these fields. If I go to the migrations file, I should also have a table called users to have these fields.

And this is just the SQL version of that, that whenever you run a migration, this would get pushed to database to have this schema now be reflected on that database. We did something called a push, which is it has the same effect but without checking, so it's more dangerous.

A migration will check for conflicts and try to resolve things and break if it can't merge without data loss. Whereas when we do a db push, it's like doing a forced push to get. It's just like, whatever, I don't care what's in the database, just push it up, I don't wanna do a check.

So we skip this and did a push, but I originally generated a migration at the start of the course and it got checked in the GitHub. So that's what this is for. This meta folder is just some meta data associated with the migrations that got created and in what order they need to run.

Migrations have to run in a certain order in which they got created. So the state of the database can be reproducible and it can go in either directions. This journal.json, whenever I generate a new migration, it'll create a new entry here for when that migration was created. The name of it and what the index is so we can keep track of it when you run your migrations.

And I guess this snapshot thing is just a text, a JSON representation of the snapshot of the schema at the time with which you ran the migration. So bunch of database stuff, you'll probably never have to do it, but that's what it's for.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now