تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Registering Background Sync0:00

The next step in the process of syncing data back to the server is to actually sync that data from the service worker. Let's see how that works. So whenever we submit the edit meter form, we of course save that data into the database, but then we call this register sync function to where we register the sync on our service worker and we give it a name of sync outbox. This is essentially setting up the sync event that's going to fire on the service worker when the client is back online. So we need to be aware of this sync outbox because that's important. If we go to our service worker, which we haven't been here in a while, let's

So we need to be aware of this sync outbox because that's important. If we go to our service worker, which we haven't been here in a while, let's scroll all the way down to the bottom so that we can register the sync event listener. This will of course get an event object and we want to inspect this tag property because this is the value that we pass to the register method. So we are essentially saying, hey, set up the sync event using this tag so that inside of the sync event listener, we can sync according to that tag. So this is very flexible. This gives us the ability to have many different syncs going on.

Adding Sync Logging1:19

So this is very flexible. This gives us the ability to have many different syncs going on. We can process different syncs if we have a need to do that, which in our case, I don't necessarily think we would, but who knows, we might. The ability is there. So that's great. Now there are many things that can go wrong here. So what I want to do is set up some kind of logging so that if we are inside of like the service worker, which in this case we are, we will know that this is a service worker because of the SW.

like the service worker, which in this case we are, we will know that this is a service worker because of the SW. But here we will say that the sync event fired, that way we just know that it fired. And then from there, we need to process, you know, the things that we have inside of our outbox. And we would do that with the trustee wait until method. And then we would pass in whatever function that we want to process that, which I think, you know, we could just call it process outbox and that's going to be fine. And I want to do something like this.

think, you know, we could just call it process outbox and that's going to be fine. And I want to do something like this. You know, anytime that I'm going to be syncing data or writing data to a database or something, I want to test it first. So I want to have a dry run that I'll set to true. That way we can test to be sure that we are reading data from the database correctly, that we are getting the data that we want. And then from there, you know, when dry run is true, which I want to set that to false. So no dry run is true.

false. So no dry run is true. So that when dry run is true, then, you know, everything's fine. We don't actually sync those changes. We just make sure everything kind of works before we sync. And then if dry run is false or if we just don't pass anything to it, you know, then the dry run will be false. And then we will actually sync that stuff. So this gives us a little bit of protection to be sure that we don't screw anything up as we are developing this.

Modularizing Outbox Processing3:10

So this gives us a little bit of protection to be sure that we don't screw anything up as we are developing this. So now the question is, where do we define this process outbox? Because our file is, it's unwieldy. And I would like to break this up into multiple different files. And at this point in time, I don't want to do that with what we currently have, just because that would be annoying for you to see. So I think now is a good time to start using our service worker as a module. So we are going to import this process outbox from a file. And we could say that we would have inside of our public folder, another folder called

So we are going to import this process outbox from a file. And we could say that we would have inside of our public folder, another folder called PWA, and then we could just call it outbox processor. That's what it is. So that's what we could call it. And then this would give us, you know, that file that we could then create. So let's do that. Inside of the public folder, we want a new file inside of the PWA folder called outbox processor. And then from here, we will just export an async function called process outbox.

processor. And then from here, we will just export an async function called process outbox. This will have, you know, that options object that has the dry run. But we would say that the default would be false if we don't pass in, you know, an options object there. But then from here, what do we need to do? We need to be able to read the database. We need to get all of the items. And then we also need to be able to delete items because as we read the database, we work with an individual item.

And then we also need to be able to delete items because as we read the database, we work with an individual item. We send that to the server. And then once that's done, we need to, you know, clean up the database. So we would need to delete the item that we are currently processing. So this means we, of course, need to work with the database. And we already have a file that is working with the database. It is our outbox.js file. It's inside of, you know, resources.js storage outbox, you know, because here we have our open DB function that's going to open the database and that's wrong.

It's inside of, you know, resources.js storage outbox, you know, because here we have our open DB function that's going to open the database and that's wrong. The on success and on error where we resolve and we reject that was defined inside of on that, that is wrong. So that would work the first time. But any other time that we tried to open up a connection to the database, it wouldn't work because we wouldn't be resolving or rejecting. So that needs to be defined outside of that. I'm glad I saw that because we would run into that. And yeah, that, that would, that would not be fun.

I'm glad I saw that because we would run into that. And yeah, that, that would, that would not be fun. So we have this file already to open the database. So it would be great if we could import this and use it within our service worker or within our outbox processor. So then that begins to, you know, take me down the path of let's use VEET to develop our service worker, which sounds great until you try it. And from a production standpoint, it works great because, you know, what do we do? We would go into our VEET config and then we would add another input here so that we have this app JS, then we would have, you know, our service worker JS.

We would go into our VEET config and then we would add another input here so that we have this app JS, then we would have, you know, our service worker JS. We would put that there and then we would output that resource so that, you know, it would be loaded, you know, well, let's just briefly do that. So we would come, we could define a route for get, which would be our service worker.js, and then we would output the service worker resources from VEET. Sounds great, and it works whenever you build something. But from a development standpoint, it doesn't work well at all. There are ways around it, but it's convoluted. It's not gonna work very well.

There are ways around it, but it's convoluted. It's not gonna work very well. So as much as I would love to use our existing outbox file, or outbox file, then it's just not worth the headache. So what we're gonna do is essentially replicate this file and use a copy inside of the PWA folder. It's not ideal, but from a development standpoint, when it comes to our service worker, it is. So inside of our PWA folder, we'll create that outbox.js file, and I'm just gonna paste this whole thing.

So inside of our PWA folder, we'll create that outbox.js file, and I'm just gonna paste this whole thing. And let's make sure that the correction is there, it is, okay. So we still want to open the DB, and here's the primary thing. The name, and the version, and the outbox store all need to be the same. So whether if it is for our service worker, or if it's inside of our VEET resources, these values need to be the same in order to read from the same database. If they're not, we're gonna run into some problems. So we still want to open up the database, but

Reading and Deleting Outbox8:07

If they're not, we're gonna run into some problems. So we still want to open up the database, but when it comes to processing the outbox, we need to read all of the items, we need to delete the items. So we have this save to outbox, which we don't need, but this is 98% of what we need in order to read the database and delete stuff. So let's call this get all outbox items. We don't need an individual item here, we need to open up the DB. We still need to set up this transaction, but in this particular case, it's gonna be a read only transaction,

We still need to set up this transaction, but in this particular case, it's gonna be a read only transaction, because that's all that we need to do, we need to read it. We still need to get the object data store, but here instead of calling put, we're gonna call this get all method. That's going to get all of the records inside of the database. So that whenever it is successful, we will resolve with our request result. But it's possible that we might not have anything there, in which case, we'll just return an empty array. That gives us something to work with that's iterable, and

in which case, we'll just return an empty array. That gives us something to work with that's iterable, and that way, the code that's consuming this function isn't gonna have to worry about that, but if there is an error, then we will just reject passing in the error that occurred, then we're done. And we can take this function, and we can make a copy, and let's just call this delete outbox item, and we need the ID. But this is going to be a read write operation, everything else is gonna be the same until we get to the actual data manipulation, in which case, we will call delete, passing in the ID, and we're done.

everything else is gonna be the same until we get to the actual data manipulation, in which case, we will call delete, passing in the ID, and we're done. Whenever that is successful, we will just resolve, we don't need to pass anything there. But if it is not successful, we will reject passing in the error. That's it, that is our two functions that we need. So that inside of our Outbooks processor, Outbook processor, anytime I say out, and it's a word with two syllables, I think of Outlook, because I use Outlook a lot, so my apologies. We want to import the getAllOutboxItems and

Processing Queued Requests10:14

I think of Outlook, because I use Outlook a lot, so my apologies. We want to import the getAllOutboxItems and deleteOutboxItem from the Outbox.js file. And then what do we do? See, the first thing we need to do inside of process outbox is get all of the items. So we will get the items, we will await getAllOutboxItems, so that then we will check. If item's length is zero, then there's nothing to process. So we will just output a message to the console from the service worker, because this is being done inside of the service worker,

So we will just output a message to the console from the service worker, because this is being done inside of the service worker, saying that outbox is empty, nothing to process. That way we know that we tried to process, and there's nothing there, so we're done. We will return. But if we do have some items, we want to sort them according to the date that they were created. So we will call the sort method, where we will compare A and B. And we want A created at locale compare. And then B created at, so that then we can iterate over the items.

And we want A created at locale compare. And then B created at, so that then we can iterate over the items. So our item of items, and let's write this out to the console, so that we can see the items that are being processed. So we will have our service worker outbox item, and then we will include just the item. And then we could check if this is a dry run, then we could output console log service worker again, dry run. And then we will continue. So if it is a dry run, then we're done.

And then we will continue. So if it is a dry run, then we're done. We have processed everything, we've iterated over everything, we're done. But if it's not a dry run, then we will try and we will catch. So if we try to make a request, where we will await, fetch the item endpoint, and then we need to pass in our options. We need to specify the method, which was item method. We need to include the headers, which will be content type. And we'll just say application slash json. We want the same thing for the except header.

And we'll just say application slash json. We want the same thing for the except header. And that's really the only thing that we need, or is it? We also need the body, and that would be json, stringify. We'll pass in the item payload, because the payload is just an object. If we look back at reading form, let's see where we save the data is right here. So where we build that item, we're building its payload is just an object. We don't serialize it there. So we need to serialize it for the body of the request that we are sending. Then we will check if the response is okay.

So we need to serialize it for the body of the request that we are sending. Then we will check if the response is okay. Then we can say that the console log service worker synced item. Then we can include the item again. And if it's not okay, we can console warn. And we'll just say that the server rejected the item. And I guess instead of using the whole item, let's just do item ID. That way, we don't need the whole item there. But one thing I forgot up here, if this is successful, if we did successfully sync that to the server,

But one thing I forgot up here, if this is successful, if we did successfully sync that to the server, then we also need to try to delete the outbox item with that ID. So that it's deleted. And then if we have an error, then we will console warn with our service worker that there was a network error. But it would be useful to know the ID at which this failed. Okay, so that's what we've done. Okay, so I think we are all okay. We have that process outbox.

Testing and CSRF Limitations14:18

Okay, so I think we are all okay. We have that process outbox. Did we actually call that? Yes, we did. But the only other thing that we need to do is go to our layout. Because now our service worker is considered a module. And it's not going to be loaded as a module unless if we tell it to do so. So down here where we register our service worker, we're gonna add another property to our options object here. And it's gonna have a value of module.

we're gonna add another property to our options object here. And it's gonna have a value of module. So the property is type, the value is module. That way our service worker is treated as a module. So let's go to the browser. Let's unregister our service worker. Let's also be sure that our index DB is deleted. Because we had an issue with the code earlier. And it would be easier to just delete the index DB, which I've already done. So what you would need to do is just right-click on the database, choose Delete.

And it would be easier to just delete the index DB, which I've already done. So what you would need to do is just right-click on the database, choose Delete. Or select the database, there will be an option to delete it. And just delete it, and then you're good to go. So let's go back to service workers. We are unregistered, Ctrl F5 to load everything fresh. And looks like we've done so without any errors. So let's go offline, let's submit a change. And we can see that index DB now has something. So field lockers right here, if you wanted to delete it,

And we can see that index DB now has something. So field lockers right here, if you wanted to delete it, just delete database, and then it's done. So if we look at the outbox, we see that we have a record, that's good. Let's add something else, so we'll change this, update it again. We can see that data may be stale, because it is. And we will refresh, and we have two records now. So we have two records. By clicking on the Update button, we have also signaled the service worker that there's this sync event that's going to happen.

By clicking on the Update button, we have also signaled the service worker that there's this sync event that's going to happen. So if we go back to our Service Workers tab here, and if we go offline, before I do that, there's nothing in the console. If we go online, and we look back at the console, we can see that the sync event fired. And sure enough, we are successfully processing everything. So we have the IDs, we have everything that we need. So we could go to our code, and we could just get rid of our options object here, so that it wouldn't be a dry run.

So we could go to our code, and we could just get rid of our options object here, so that it wouldn't be a dry run. Now, you're probably thinking that's not going to work, because it doesn't matter what kind of request that we are making. It's not a GET request, it's either, well, in this particular case, they would be PUT requests. But we didn't include a CSRF token, so these requests are going to be rejected anyway. Which I guess we could demonstrate that, but we know that that's going to happen, because there's no CSRF token with the request that we are sending.

Which I guess we could demonstrate that, but we know that that's going to happen, because there's no CSRF token with the request that we are sending. And that was purposeful, because this gives us something to think about. So if a client is offline for a long enough period of time, even if we stored the CSRF token whenever we stored the database in IndexedDB, that's not going to work, because the CSRF token is linked to the session. If the session expires, nothing is going to work. So what do we do then? Do we just accept any POST, PUT, PATCH, or DELETE requests without any kind of token?

Do we just accept any POST, PUT, PATCH, or DELETE requests without any kind of token? No, absolutely not, that's the very worst thing that we could do. But one thing we could do is use sanctum tokens, because we could scope tokens to a particular action. And we can protect API endpoints based upon those actions. It's not perfect, but it's really the best option that we have. So let's see how we can implement that in the next episode.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟