December 16th, 2024 × #local-storage#offline#privacy
Local Data: Sqlite, LocalStorage, Session, Cookies and IndexDB
Discussion on different ways to store data locally in the browser for things like user preferences, allowing app usage before signup, faster data access, privacy, persisting data on refresh, and storing auth tokens.
Transcript
Scott Tolinski
Welcome to Syntax. On this Monday Sanity treat, we're gonna be talking about local data. Locally, right now, Wes, I'm trying to get a snack in. I don't know if you've ever had these rice mochi balls from, Trader Joe's, but holy cow, they're my favorite thing in the whole world.
Scott Tolinski
It takes great restraint for me not to eat them.
Scott Tolinski
That's why whole bag. Yeah. Yeah. I don't keep them locally in my office usually because then I'll need to access them all the time.
Scott Tolinski
But if, you need to access your data locally, we're gonna be talking about that in this episode. But what happens if you need to access your data locally and that data's not there and, you're not checking to see if it's there correctly? Well, you're gonna get a bug in your site. And oftentimes, you know, with this stuff, especially if we're talking about syncing engines, making sure the data is where it's supposed to be, Yeah. We can often get in these situations where we think we're in the happy path all the time. And, oftentimes, we might not even realize what that happy path is. I experience that all the time on my HabitPath app because I use local data and data syncing. And, man, it is hard to solve those bugs, but not with Sentry because Sentry lets me know who has what issues at any given point in my application, and I know what's happening beyond the happy path. I it works for me. All my data's local. It works. Nothing but sad paths in the in your century. Just a sad place to be. But You know what? The best part is too. You know, Courtney also uses Habit Path. And, just like most things, when other people use your stuff, it's really nice for them to be like, this is broken. And then I'll go check the Sentry, see what's going on, fix the problems. Head on over to sentry.i0forward/ syntax. Sign up again 2 months for free with the coupon code Sanity treat.
Scott Tolinski
Sentry saves my bacon all the time. Wes, what's up, my man? How's it going? Hey. Let's talk about,
Wes Bos
storing data locally. There's lots of ways you can store data in the browser, but let's talk about why you might wanna store data in the browser first. Right? Probably the first one is, user preferences and user settings.
Storing user preferences/settings locally
Wes Bos
You don't necessarily always have an account for somebody to store that in, or it doesn't make sense to always send that data back to the database every single time that it changes. Right? So one example I'll have is on my course platform, as people are watching the videos, every I forget how long. Every every time the video current time updates, which can be every 2 to 3 seconds, I'm putting that data in local storage. And same same with, playback rate, volume level, like, all those settings. Right? I'll stick that in local storage, and then, periodically, they'll they'll get synced to to the back end. But, it's nice to stick that in local storage or or in the browser when you need it immediately.
Scott Tolinski
Yeah. Another thing too often that, like I think you see this a lot more native apps, which is something that people, might be doing on the web more and more these days is letting you use the thing before signing up for an account or even maybe paying for data syncing. That's like a thing where people pay to sync the data, and they say, oh, actually, I need to have this available on all my devices.
Scott Tolinski
You can store that data. You can let the the people use the application even before signing up for an account or even choosing to sync it to a server somewhere. And that's just, like, straight up usage of your application.
Faster data access from local storage
Scott Tolinski
If it is like a to do app, you're physically saving all the mutations and doing everything in the local data. And in that same regard, you might be saving that data locally from the server into local storage or into your IndexedDB or local storage or literally anything.
Scott Tolinski
So that way, you can access it faster. Because if the data's already on the client, you don't need to go to the server to get it. So it's a way of accessing faster data as well. Sometimes it can it can even just be a privacy thing. Yes. So I built a
Privacy for user data
Wes Bos
background removal tool.
Wes Bos
And the thing with people's photos is they don't want to upload them to some random sketchy service that they're they're gonna have. Right? So this background removal tool will just do it entirely in the browser.
Wes Bos
And the thing about that is if you refresh the page, they're gone. So you can stick those files and stick all the data related to that in the browser in in one of the places that we're gonna be talking about. In that way, when you come back and refresh the page or, or even, like I was on JS bench the other day writing some benchmark tests for JavaScript, and I, like, refreshed the page before I had, like, saved it.
Persist files/data on refresh
Wes Bos
Or, TypeScript Playground is another good example.
Wes Bos
Before I've saved it, right, before it's in in their database, if they even save that in their database, then it's it's gone.
Wes Bos
So it's nice to just persist it locally in the browser before you even have decided to send that to to a server.
Scott Tolinski
Yeah. I think anytime, like you said, anytime that you would expect your application to, if you hit the refresh button for it to remain in the same state as when you left off, you're filling out a form, you're shopping at a store, you're you're you have a draft of a tweet, those types of things. Right? Anytime you want that state to persist is a perfect opportunity to save your users some headaches by putting that that information in local data. Let me tell you. There is nothing more frustrating than filling out a big ass form and having the page refresh for some reason and having to start over.
Scott Tolinski
If you are doing job application things Wes they ask you to fill out every single one of your experiences or any of that stuff, you gotta have that data, storing locally or something so that way the user isn't losing that. That drives me absolutely crazy as either. Yeah.
Store auth tokens locally
Scott Tolinski
And your users will delight be delighted for it. I'll tell you that. Another Node is auth tokens. You know, we don't often think about this, but anytime you log in pnpm anything, it's either storing it in a, cookie or local storage typically, whether it's a session token or a JWT, as an access token, those types of things. These are stored locally on your client.
Scott Tolinski
Reason being is, anytime you have a request to that server, you need that information there at the initial request. That way, the server can be able to authenticate it JS you go or authorize it. Man, I don't know about you, but I I flip flop authorization and authentication so much. I'm I'm talking with the XeroSync folks all the time, and I'll use the word auth authentication, and they'll be like, you mean authorization. Right? I'm like, yes. I do. Gosh darn it again.
Wes Bos
Flip flops to my dad for something. That I would use the word authorization.
Wes Bos
Authentication is is where I'm at. Authorization is like a like a credit card thing to me. No. No. No. They're they're they're,
Scott Tolinski
very distinct in web development or even any sort of login system. Right? And I'm gonna mess this up. I, authentication is logging in. Right? It's the process of logging in, and authorization is the process of making sure you have access to the things you have access to. That makes sense. Yeah. Totally. I always just goof it up, though, when I'm I'm trying to say it quickly.
Scott Tolinski
Let's talk about where you can store your data. Alright. So
Wes Bos
first one is cookies. Storing data in your user's browser, you can throw them in cookies. There are HTTP cookies, which can only be set and read via the server and server Wes, and then there are just regular cookies, which can be set and read via JavaScript as well.
Wes Bos
Cookies have a limit to how much data you can stick in them. So, generally, cookies are only used for things like language settings, session IDs, just like identifiers in short little text settings for that specific user.
Scott Tolinski
Yes. Next up is local storage. Local storage is often more used for single values like, a JWT, which could then be encoded,
Wes Bos
decoded, those types of things, or a simple string value. Or you might use a local storage for preferences, anything that you need to just save. Again, typically, just a single string, save it, retrieve it, those types of deals. Yeah. Local storage is you can fit a lot in there, but the the thing about local storage is that it's it's only text. Right? It's key value store. So if you are trying to store something that is not text, you have to convert that to text. So if it's an object, you JSON stringify it. If it's an image, you you can store images in local storage by converting to base 64.
Wes Bos
It's probably not the best bet, but it's Mhmm. It's a really simple API where you can simply just say local storage Scott set item, local storage dot get item. You can pull it out.
Wes Bos
The only thing that that gets me sometimes is I'll pull something out of local storage and assume it's an object and assume you can JSON parse it, but it's not, not always the case. Right? Sometimes you accidentally set undefined and then try to JSON parse undefined, and you're in trouble. I'm almost always saving things in cookies over local storage,
Scott Tolinski
for no particular reason other than I just do. But nowadays, if I'm storing data, like, beyond single values or whatever, I'm I'm using, IndexedDB typically.
Scott Tolinski
There's also session storage. So session storage, just like local storage. However, if you close the browser, you close the tab, that's the end of the session, and it goes away. I I've never used session storage. I'm going to be straight up about that. I just haven't hit a situation where I needed session storage, but I I'm sure there's plenty of of these reasons to use it. I think a a good example of session storage would be
Wes Bos
you were just talking about like, you're filling out a form or job application.
Wes Bos
You wanna be able to refresh the page and that data to persist.
Wes Bos
Session storage will persist in the same tab if you refresh the page. It'll not if you open up a secondary tab, and that's really important if you Yeah. Somebody is potentially on a shared computer. So you imagine a job application. Somebody goes to apply for a job.
Wes Bos
They fill out the thing, maybe they don't finish it, and then they close the tab. If you put that in local storage, somebody else comes around and and opens up the same website, then there's a bit of a privacy issue because now you're sharing your your data between users of the same device, whereas session storage would as long as they close the tab, you will
Scott Tolinski
you'll be fine. It's cleaned up. Yeah. That makes way too much sense. So, thank you for that.
Scott Tolinski
Next up is IndexedDB, which, like a lot of people, I think, they say that it's hard to work with. And, honestly, I've used it just, you know, raw straight up API, and I didn't find it to be that awful.
Scott Tolinski
It's it's not like the most fun API. It's not as simple as just, get set whatever. However Mhmm. I I do use Dexi quite a bit. Dexi is a a small library on top of IndexedDB, and it's basically just giving you a nice little API for creating, getting updating data. And, you know, you have to do a little bit of setup, but it's more like working with things that your typical, like, normal databases that you work with. And for the most part, I found Dexi to be Node to work in. And and pretty much anytime I need to store structured data, and objective data, I'm not picking cookies or local storage. I'm picking, IndexedDB,
Wes Bos
obviously, because that's what it's made for. Query local storage. Right? You can simply just check if a key is there or Node, whereas IndexedDB is a database, and you can query it and filter and do what all the stuff you're used to. And I I picked up Dexi. Js for that background removal project I did, both to store files, which the browser also has file storage. We did a whole episode on that, but you can also just store, like, blobs of data in IndexedDB. Yeah. And the cool thing about it is it has, like, React hooks and and Svelte hooks so that as long as you save it to IndexedDB, the your application is reactive, and it's it's it's your whole state store.
Wes Bos
And it's it's a really cool
Scott Tolinski
project. I'm a big fan of it. I wanna introduce you to a library from, Kyle Simpson, of you don't know JavaScript fame. He has a library. He has, like, a kind of a a bunch of new packages called BYO JS that are all kind of cool. And one of these is a storage package called, storage, byojs.devforward/storage.
Scott Tolinski
And the stated goal of this project is to have a simple key value storage API for all of the storage mechanisms.
Scott Tolinski
So you have a basic git pnpm, and the only difference is is where you import those from. So you import it from IDB, for IndexedDB, from local storage, your session storage cookie, o p f s, which is, the private file system.
Scott Tolinski
And so, yeah, basically, it just gives you the same API to work within all of this stuff. And I thought this was a really neat API that you don't really hear a lot of people talking about. That's cool. It seems like it's only key value, though. Right? So you wouldn't be able to key value. To query. You know what I really like about some key value stores? Deno's key value does this, is,
Wes Bos
you can set an array of keys.
Wes Bos
So you could say, like, user and user 123.
Wes Bos
And that way, you'd be able to query all the users or just query user 123.
Wes Bos
Yeah. I'm I'm sure more key value databases are like that. Sure. But that's Deno's key value store is is 1. It's built on top of SQLite. Oh,
Scott Tolinski
there's also a host of really, wild new options that, people are exploring in this area right now. As we start to get, like, more down this rabbit hole of using local data more frequently in applications, People are wondering, what can we do that is beyond IndexedDB? So a couple of these things are are using WASM. So one of them is SQLite via WASM, is increasingly more of an interesting option.
Scott Tolinski
Although, you know, who said, I I believe there is, like, a penalty, essentially, like an initial download Sanity, where the 1st time you you load it up, there is a pretty large hit to use something like this. You have to download so the way
Wes Bos
what it's doing is it's literally running SQLite in the browser. Mhmm. So you have to download the entire binary to run SQLite via WASM, and I'm curious how large that actually is.
Scott Tolinski
Oh, holy smokes. Four 10 KBs? How big? 410 kilobytes.
Wes Bos
Yeah. 558 k kilobytes, by a 1.14 megs if you want the async version. I'm not sure what that means, but that's much smaller than I would have thought to be able to run SQLite in the browser via WebAssembly. I I think there's also, like, going between the browser and WebAssembly, there's, like, a cost you pay for going across that bridge.
Wes Bos
But, we had Johannes Schickling on, and he's one of the devs behind, WebAssembly SQLite.
Wes Bos
And he knows the stuff, so I kinda kinda trust him with this. Yeah. There's also, another one, pg lite, which is Postgres
Scott Tolinski
in WASM.
Scott Tolinski
And, that's being created by the folks over at Electric SQL, which is one of these cool new local first platforms.
Scott Tolinski
And I think Drizzle even has support for pg lite now. Man.
Scott Tolinski
Yeah.
Scott Tolinski
So, man,
Wes Bos
That's what I want. I don't wanna learn IndexedDB.
Wes Bos
Oh, come on. Dexi I already know. Well, I, like, I I can learn it, but you know what I mean? Like, I wanna be able to just use the same database queries everywhere. Totally. And especially even if you're sharing schemas.
Scott Tolinski
ESLint drizz schemas.
Scott Tolinski
Drizzle does have they even have a whole page, which we'll link to, which is, Drizzle and PG lite, how how you can use the 2 of them together, again, to do your drizzle DB selects and all that stuff, directly from PG light, which you know, the big question I have with some of these still is that because I haven't used any of these more intense ones JS is migrations and how do you handle that kind of stuff in a client side level? I guess you're running the migrations on boot up or something. Yeah? I don't know. Yeah. Probably. I I bet it's you try to keep them
Wes Bos
a little bit more flat.
Wes Bos
So I I guess you don't have to, but yeah. That's a a great question. You'd have to run it in the in the user's browser because that's
Scott Tolinski
that's where it exists. Yes. So that's all great. Right? You can store data in any of those numbers of ways. So, you know and and primarily Wes you're doing this stuff, especially in applications that are loading this data, you can think of a lot of the times that this data, since it's living on the client, is a use case for not waiting for a long server response.
Scott Tolinski
Because if the data JS living on the client, right, you don't necessarily always wanna wait for the server to hit the database, to do all the database stuff on the server before returning HTML.
Scott Tolinski
You wanna get the client side HTML and JavaScript as fast as physically possible, then the data's there. It doesn't take any time to read it from the local database. It's infinitesimally small to load from a local database compared to, loading from a database on a server and building the HTML. So you're doing a lot more client side rendering, which is one of the reasons why I've been talking about client side rendering more. It's that you you're sending the information to the client. The client is then grabbing the data, loading it up, getting you all ready to go. And then, occasionally, if you need it in the background, you could be fetching the data from the database. That database process on your your server side database could be kicked off right away. So that way, by the time that that comes back, you you initially have data. You initially have a loading thing that's beyond a blank skeleton screen, and then the rest of the data can be filtered in in any sort of way. Now you could have, like, a little syncing message to let people know that data's coming in, or you could just pop it in, which might not be a a great solution if you're you're getting your UI popping all over the place. Or you could have a button that says, you know, there's new data refresh, those types of things. But in reality, I think what it comes down to is that once you get into syncing data to a server, you're gonna wanna do it a couple of ways. Here's how I've done it specifically with storing things in Dexi.
Scott Tolinski
You store things in Dexi, and it's stored primarily in your your client side, database. When you make a change, you push that change up to the server. When you load your application, again, you fire off a message that gets then pushed into your, your local database. That database updates the UI, and then that information is in your your local database. You're just trying to keep those 2 in sync. At a basic level, for an application that 1 person is using, you're sending a message off on 1 device, whatever. That's very simple.
Scott Tolinski
We talked about how it can get really complex with conflict resolution.
Scott Tolinski
But the way these things will work, I believe, in the future, specifically, a platform like XeroSync does this really well. It stores your information locally, and it handles that sync process.
Scott Tolinski
And you're only ever writing 1 Node set of queries or you're writing 1 set of, mutations all from the client side,
Wes Bos
and it just knows what to do. It's sending messages back and forth. You're not being like, oh, you're not writing the glue code yourself to sync it and query from the right place because that sounds like a a really hard thing to to do yourself.
Scott Tolinski
Yes. And one of the cooler things that many of these new libraries are doing is that instead of, like, the naive way I've done it the naive way a whole bunch of times. If you need this sync, by the way, there's plenty of application where you just save things locally, and that's it.
Scott Tolinski
That that's definitely super valid, so you don't always need a sync. The the naive way is just to send all the information every single time, and that's too much information to send every single time. But if you're not dealing with a ton of data, not a huge deal. But the more and more data you add to the system, the larger and larger those sync messages are going to be. So a lot of the things that the the new platforms are doing is would be, like, patch messages.
Scott Tolinski
So it's only sending the data that's changed. So it's asking for what's changed since this version. Oh, this has changed. That way, that sync message becomes smaller every sing Scott every single time, but it's smaller than it would be if you were sending all the data, meaning that that trip becomes much less, meaning that you don't have that crazy pop in or anything like that because it's not sending a massive bundle of data that then needs to get updated into your local database.
Scott Tolinski
So that is how you get it done with local data. Local data, I think, JS, widely underused, but I think it could be, you know, greatly more used in terms of, like we mentioned, keeping persistent state for things like forms, surveys, things that would piss the user off if, if they lost their data in between save or refresh.
Wes Bos
Can we rattle through a couple of the the possible options for this? I know we did a whole show on it, but, the author of tiny Bos, when you were explaining it to me, I was like, I don't really understand where this JS, and he said he explicitly added a diagram to the docs for us.
Wes Bos
And it's exactly what you're just explaining. Right? Like, it's it's it's local, but you can also synchronize.
Wes Bos
Like, it's a local database ish, but then it will also synchronize, and it can also persist to, like, a Cloudflare or something like that.
Scott Tolinski
Yes.
Scott Tolinski
And, I I believe James was on the Local First FM, Local First podcast, and gave a really great explanation about how tiny base came to be. So that's well worth your time to listen to. But, yeah, tiny base is an option here.
Scott Tolinski
RepliCache, I I've talked about Replicash before. It is tough. Replicash is becoming 0 sync, which, in my opinion, is way, way, way easier than Replicash.
Scott Tolinski
PowerSync, man, PowerSync is one of these ones that handles the syncing. But every single time I look at this platform, again, I I there's too many graphs and diagrams to the point where I'm like, alright. What are you doing for me here? There's yeah. It's tough.
Scott Tolinski
Rx DB is a cool one as an option for again, the ones that make this the easiest are the ones that kind of combine the 2.
Scott Tolinski
Instant DB is a closed sourced Firebase competitor that does local storage by default. That's pretty amazing.
Scott Tolinski
Electric SQL has kind of undergone some changes lately Wes they're they're kind of, like, they're changing somewhat significantly. I I know it was, like, become version 2 now, and I haven't checked in on it, since they announced that it's going to to change a little bit. But Electric SQL, like we mentioned earlier in this episode with the, PG lite has definitely been one of the bigger, innovators in this space along with the RepliCash folks and many of these platforms. Honestly, tiny base as well, for sure. There are a lot of these options that we've talked about in here. Yeah. I'm gonna paste in local first web dot dev. It has a whole section on different software for these things.
Scott Tolinski
Yeah. I I think there's a lot of different opportunity here. My the most exciting one to me is Xero because I've been, it's kind of the thing I've been, like, really hoping and wishing for. Working with. Yeah. And I've been using it for a little bit. Granted, there could be other ones. Like, Instant DB is a really cool option, but it isn't like I can't spin up an Instant DB server myself. And and that's the only bummer about that. Right? You're you're tapping into their ecosystem to to get going. But when I wrote an app with the Instant DB, it was probably the closest thing I had to get 0 to 100, in the space because it comes with, auth and MagicLink auth and all this stuff. Like, you could have it up and running in no time. You're just not able to run it yourself at the moment.
Wes Bos
Awesome. Well, that's local data. Hopefully, you learned a thing or 2. Let us know if you have anything to add. Tweet us, Scott, email us, whatever you got. We're at syntax f m. And,
Scott Tolinski
that's it. Yeah. And let us know what you're using local data for. If there's anything that we didn't mention in this episode that you think is a good use case for storing local data, Leave a comment below, YouTube, Spotify, wherever you're at.
Wes Bos
Alright. Peace.