955: SvelteKit Has Solved Data Loading ![rw-book-cover](https://wsrv.nl/?url=https%3A%2F%2Fmegaphone.imgix.net%2Fpodcasts%2F5197fe5a-42f7-11f0-affd-87d9985a1760%2Fimage%2Fc86a54acd72683732c4773e25bf0ae14.png%3Fixlib%3Drails-4.3.1%26max-w%3D3000%26max-h%3D3000%26fit%3Dcrop%26auto%3Dformat%2Ccompress&w=512&h=512) ## Metadata - Author: [[Syntax - Tasty Web Development Treats]] - Full Title: 955: SvelteKit Has Solved Data Loading - Category: #podcasts - URL: https://share.snipd.com/episode/591a43ab-c4c4-4366-a836-6180eca903b6 ## Document Notes ## Highlights - **Function-Style Server Calls** - SvelteKit remote functions let you call server code as plain functions from components, removing route-based load hot-potatoing. - You can await queries directly in components or use query.loading/error/current for reactive UX. Transcript: Scott Tolinski Welcome to Syntax. Today, we're talking about remote functions in RPC in Svelte. I'm going to say, off the bat, this is the solution for RPC, for data loading, for anything. I don't want to use anything else on any other platform, and we're going to show you why. If I'm using React, it better look like this. So we're going to be breaking down what these things are, just the general approach for data loading, how it kind of takes the server client side of it all and makes it really nice and palatable While keeping a nice separation still. My name is Scott Talinsky. I'm a developer from Denver. And with me as always is Wes Boss. What's up, Wes? Wes Bos Hey, excited to talk about this because I dove into several of these like rpc libraries in the last uh couple weeks you know and um it turns out better auth has one called better call um Hano has one eliza has one cloud flare has one that's called cat cat and web or something like that. Like it's Cap and Crunch. And like, there are a lot of the, like this idea of like, obviously we've talked about TRPC. We had them on the podcast as well in the past, but there's so many different implementations of this RPC. And I'm excited to see what Svelte's take on it is. Scott Tolinski Yeah, this is something that I think we've both personally felt. The Syntax site is built with SvelteKit. It uses the load functions for data. And I think one of the things that always felt odd about that was like all of your data, all of your refreshing, your invalidation, even your form actions, they all kind of live at that Root of the route level on any given route it exists on a page level and like in a componentized world that just felt weird right sometimes you were defining actions on a route and then Having to call them or refresh more data than you'd want or reinvalidate a load function. So I was really excited when they announced this at Svelte Summit. And I've been doing quite a bit with this, like really quite a bit. In fact, Wes, you might be interested to know, I've been going nuts on the new syntax website. So we've been working on a new syntax website. Wes Bos I've been getting emails, brother. Man, you're motoring. Scott Tolinski I motoring and it is like ah i ripped out our orm so we're using drizzle now i changed from my sequel to uh postgres i rewrote all of the data loading and felt remote functions i have done A shocking amount of things and courtney was like is this stress you out? Do you regret making these big choices? And I said, no, I love this. This is what I love. Scott's middle name is Rewrite Talinsky. Did you know that? I love taking everything out of the cabinet and putting it all on the countertop and then meticulously putting it back in and maybe 3D prints and boxes or something. And that's what I'm doing right now. Wes Bos I just like pushing the stuff to the back of that. Like we lived in our house for like three years now and I like got on up on a stool. We have this really deep cabinet and I was finding like hot sauce from like three or four years ago. I was like, I should probably do a Talinsky on this right now. Scott Tolinski I love taking it all out. And then in the web dev is absolutely the same. But you know, at this point, we I needed to like rework the entire data structure of the website anyway. So it was like, yeah, if I'm already reworking the entire schema and how the data is connected, and like, I might as well do it in technologies I actually like, if it's easy enough to just Migrate it. Yeah, which it has been. Yeah. So it's been it's been a trip. But let's talk about remote functions because I've been using these things like crazy. Basically, as of today, when we're recording this, this may change soonish. To get these things going, you do need to tick two different experimental options in a Svelte config, remote functions true and async true. So these APIs are used in combination the async Svelte where you can just throw a weight tags in your Svelte components and just do that. And it just all works. Remote functions, though, remote functions exist where you have a special file name, it's something dot remoteote.ts or js. These can live straight up anywhere. And a.remote file name is great because that lets you know off the jump, this is taking place on the server. You don't have to worry about client server, where's this code running? Am I importing something like a database or something insecure in this? You know that it's a dot remote. It's a server-based file. So that's really great, right? And then from within a normal Svelte component, you can just straight up import that query, mutation, whatever. We'll talk about those in a second. But you can just straight up import those as a function and call that function in your code. So if you want to, let's talk about the like the first one of these, which is like querying data. In a dot remote function, you have a query, it's essentially like a query function that takes in another function as a parameter, or like a schema that you can then use to validate your Data. Either way, you have a function, that function can do database calls, returns data. In the client side, you're just calling that function, bingo, your data's there. ([Time 0:00:00](https://share.snipd.com/snip/d2af8e33-8ecb-4da3-b3ed-5af37612ca5e)) - **Await Anywhere In Components** - Async Svelte plus remote functions enable awaiting data at the component level, improving server/client rendering ergonomics. - This removes the need for useEffect-style patterns common in client React for simple fetches. Transcript: Wes Bos Over the network if you're calling that from client to the server. Or if it's server rendered, it's just going to run it all on the server. Is that a fair description of it? Scott Tolinski A perfectly fair description of it. Now, when this gets really powerful is combining with the fact that you can just await anywhere inside of Svelte components. So you could just even inside of a loop, you could do like an each await, get Scott, and then as the whatever. And the next thing you know, your data is just there. Or at the top level of a component, you can await in the top level of component and get that data there as well and utilize it just like you typically would. And you don't even have to await it either. You can just run the query. And then you could also have a query dot error, query dot loading or query dot current, which then gets you access to the data when it's available. But if you want like server side rendering and stuff, await it, and it just works. ([Time 0:06:54](https://share.snipd.com/snip/5321abb3-ad21-4a10-9936-4cfd282a2443)) - **Use Standard Schema For Validation** - Use Standard Schema validators (Zod, Valibot, etc.) as the first parameter to remote queries to get unified validation and types. - This yields seamless TypeScript types and consistent client/server validation without extra type imports. Transcript: Scott Tolinski You can also pass an argument. So these things are functions, you can pass in arguments. One really neat thing that they've done with all of the remote function stuff is that they've really leaned on standard schema. If you want to learn more about standard schema, we did a whole video on it. But basically, because it uses standard schema, that means that you can use any validation library out there to validate your inputs. So whether that's Zod, Valabod, archetype, like whatever you want to use that uses standard schema, you can so. And inside of your query, if you want the if you want to accept arguments here, the first parameter is just that validation. So in all of their docs, they use Valabot, which is really great. Actually, I've been using Valabot, it's really nice. But if you could say like the query, the first parameters of v.string, then you know that that requires a string. And then of course, you get all of your type script type checking, you get everything like, really nice. I think that's one of the things that as you'll see throughout this episode, is that the type checking story for all of this is so effortlessly good. ([Time 0:10:14](https://share.snipd.com/snip/d5226224-9dfd-4967-bf8f-b8fbda0b69ee)) - **Batch Multiple Queries Automatically** - query.batch groups queries issued in the same microtask into one server call so many per-item calls become a single batched request. - Batching reduces network trips and can consolidate multiple inputs into one DB query. Transcript: Scott Tolinski There's also query dot batch, which allows you to batch queries that happen within the same micro task. This is like in their example, they basically had a list of cities, and then they had a query that was like get weather from that city. And the query was taking place inside of an each loop in the component. So inside of an each loop, the city had a wait to get weather passing the city that's being passed in from the loop. And so if you were to do this without the batching, you could imagine each loop is going to be a call, right? A remote call. And that's not great. So if you use query dot batch, it's going to batch all of them from the same task. And therefore, it's going to not make a bunch of trips. Basically, it calls them all instead of having multiple calls. Wes Bos So the benefit here is that like, instead of sending off 10 requests for 10 cities, it sends an array of 10 requests once. And then those 10 things are executed on the server. And I guess you could have logic on your server to do them all at once, or you can just fire them all off at once. And then it sends all the responses back at once. So that helps both with network, but also I'm assuming it helps with rendering as well because then you're not getting 10 render updates as they come in. You simply just get one render once it's all done. Scott Tolinski Yes, and in this particular example, they're able, because it's in that loop, they're doing query.batch. ([Time 0:13:30](https://share.snipd.com/snip/4fb754f3-02ca-495e-9a5c-b812145e7454)) - **Use Form Functions For Mutations** - Prefer form remote functions for HTML form mutations to get progressive enhancement, field-level validation, and built-in error handling. - Spread mutation.fields onto inputs and use mutation.enhance to programmatically hook into submit lifecycle. Transcript: Scott Tolinski There's a another type of remote function called a form function. And the form function is the goat. This thing is awesome. So what's so cool about this is it's used for doing mutations, but not just general mutations. It's useful for doing mutations that take place on an HTML form. What this gets you is you're basically defining a form function using your standard schema stuff. You're passing in the inputs as the first parameter. The second parameter is an async function that does the work. But what's so awesome about this is not the server side stuff, because the server side thing functions exactly like you expect. Data comes in, you do the work, you return your stuff. But on the client side, the form function has a ton of stuff. So you spread the remote function onto a form, and then you get a progressively enhanced HTML form that works with or without JavaScript. You can spread out individual fields, and then you get individual field validation. So you write your standard schema on the server side that is then used to validate both the input into the function as well as the HTML input itself. Wes Bos Wait, so it does like it will render out the client side HTML and the JavaScript that is necessary for it to client side validate? Yes. Oh, that this is almost getting like, like a rails. It's sick. Scott Tolinski It this is sick. Because not only do you get that, right? So like the way you're spreading it out, you're spreading like, let's say it's a create post, you're spreading create post dot fields dot title as text on a normal input tag, that then Sets the input tag to type of text. It sets the ID, it sets all of the stuff that you would need to have a valid working form input. And that rules, man. I'll tell you what, I was working on a blog. And I use this in like I'm doing a tutorial course on which is it's going to be out by the time you are watching this so check it out on syntax if you want to learn how to do this but like man to Be able to build this wrapping a label around an input and just saying here use that oh it's read only like man this kicked ass I really, really liked working in this this way. But it gets even better than that, because not only do you get those things, man, you get so you get the validation component and stuff like that. But you can also get error messages. You can get sorry. I'm just going to stop you here. Wes Bos Couple episodes ago you were fighting me and saying i was i was showing you some form library that like yeah that did this yes and you're like i don't need that i could just do that with this Felt store and a couple lines of code yeah and now that's what i'm doing here you're gushing over exactly this no this is the platform. Scott Tolinski Mind you, this existed when we recorded that episode. This is less code than that was, too. I'll tell you that. My point was that if I want, not that I don't want a form library, it's that if I have a form library, I want it to do more for me. I don't want it to write all of the inputs. I don't want to have to write the update stuff. I don't want to have to write any of that stuff. Because at some point, it's like, what is the library doing for me? I want the library, or in this case, the platform to do more for the forms. Wes Bos Yeah, I want to pass it in a giant object schema to exactly inputs, validation, all of that stuff. And yes, I think that's that's what that library did. I can't recall the name of it right now, but I agree with you because I am done writing CRUD forms where we simply done data. Done. Yeah. And like, I do want to push back on that because I agree. Scott Tolinski Like, I do think form libraries are really nice. I've built my own so many times, but I want the form library to do more. If I'm having to write out every little bit. Wes Bos Tip to tail. Scott Tolinski Yeah. Yes. Then at that point, what that library did, I didn't see a benefit of it. Yes, it handled validation in those types of states, but you still had to write most of that code yourself. A lot of that was because of how you work in forms in React in general. So that that's my point. Also, this gives you client server gives you the whole whole bingo bango, gives you everything here. There's also you can do programmatic validation with it easily enough. If something becomes invalid, you can get the error message directly from here without having to write any of that. You don't have to have controlled inputs or anything like that. The state just kind of lives as a part of this remote function. So you don't have a controlled form. You don't have to deal with form data on yourself. You're just writing an HTML form that works in JavaScript or without JavaScript, and you get access to things like the individual error messages if something becomes invalidated. It's pretty sick. That's great. Wes Bos I could see myself building even higher level wrapper on this. I'm already doing that, yes. At a very high level, just took all my inputs, figured out what kind of input they needed to be. Is it a text area? Is it a number? Is it a date? Et cetera. Scott Tolinski I love doing that. I'm already doing that. Yes. Huge, huge fan of that. Yeah, it's really neat. And like, obviously, if you want to step in, and there's this whole progressive enhancement story, if you want to step in and do more with JavaScript in here, you can do a dot enhance on Here and get access to the entire process so let's say you want to throw toast message after you submit it or you want to handle errors your own way or you just want to step in there yeah you Wes Bos Can hook in you can call await submit you can call a form.reset if you want to um and i assume that's the same as if you need to do something custom on like key up or something, you could you Can overwrite the default one. Totally. Scott Tolinski You can also get in here and do you can update the state programmatically of the form. Keep in mind, again, these aren't like controlled forms in that way. But you can get on here in an individual field by referencing mutation dot fields dot the field name so dot title dot set and you can programmically update that. And you can grab the value at any point with fields dot value and get access to that value live. So it ends up becoming a controlled input. But you didn't write one line of state, you didn't write a state updater, that state just lives with the form. And it all it's just so cool. ([Time 0:15:29](https://share.snipd.com/snip/f2339cce-2a14-4961-83fd-4d8003ca4f27)) - **Forms Manage State For You** - Form remote functions manage field state and validation so you don't need controlled inputs or manual state. - You can programmatically set field values or read fields.value from the mutation API when needed. Transcript: Scott Tolinski If something becomes invalid, you can get the error message directly from here without having to write any of that. You don't have to have controlled inputs or anything like that. The state just kind of lives as a part of this remote function. So you don't have a controlled form. You don't have to deal with form data on yourself. You're just writing an HTML form that works in JavaScript or without JavaScript, and you get access to things like the individual error messages if something becomes invalidated. It's pretty sick. That's great. Wes Bos I could see myself building even higher level wrapper on this. I'm already doing that, yes. At a very high level, just took all my inputs, figured out what kind of input they needed to be. Is it a text area? Is it a number? Is it a date? Et cetera. Scott Tolinski I love doing that. I'm already doing that. Yes. Huge, huge fan of that. Yeah, it's really neat. And like, obviously, if you want to step in, and there's this whole progressive enhancement story, if you want to step in and do more with JavaScript in here, you can do a dot enhance on Here and get access to the entire process so let's say you want to throw toast message after you submit it or you want to handle errors your own way or you just want to step in there yeah you Wes Bos Can hook in you can call await submit you can call a form.reset if you want to um and i assume that's the same as if you need to do something custom on like key up or something, you could you Can overwrite the default one. Totally. Scott Tolinski You can also get in here and do you can update the state programmatically of the form. Keep in mind, again, these aren't like controlled forms in that way. But you can get on here in an individual field by referencing mutation dot fields dot the field name so dot title dot set and you can programmically update that. And you can grab the value at any point with fields dot value and get access to that value live. So it ends up becoming a controlled input. ([Time 0:20:19](https://share.snipd.com/snip/920d755b-4701-4990-a3ca-719789bd9864)) - **Update Queries Without Extra Refresh** - After a mutation, use query.set with the returned data to avoid an extra database refresh call. - Use optimistic updates where appropriate to keep UI snappy. Transcript: Scott Tolinski When you're done with these, you can update your data. So there is the dot refresh method that we called before. If you run dot refresh, it is just running that query again. But there's also a dot set method to on that. So let's say your mutation returns the new data, right? Like you didn't update and that mutation actually returned the new data, you wouldn't have to then call a refresh and have it do another database query, you could use this dot set to take That new data and just insert it as the result into that query and pass that along, avoiding an extra database call. They really thought of like everything here. ([Time 0:22:31](https://share.snipd.com/snip/916f990d-e76a-468c-aa4b-f136e3c927c1)) - **Five Remote Function Types** - Svelte remote functions provide five flavors: query, query.batch, form, command, and pre-render to cover common data patterns. - Pre-render runs only at build time for static generation. Transcript: Scott Tolinski So if you're doing static sites, you only wanted this to ever run on build, you have the option to have the pre render. So that's it you have query, query batch, form, command, and pre-render. Those are the different remote functions. ([Time 0:24:32](https://share.snipd.com/snip/09958e48-750e-4603-a57f-d67886fa6955)) - **Rewriting Syntax With Remote Functions** - Scott rewrote the Syntax site data layer to use SvelteKit remote functions and Drizzle, enjoying less code and better type safety. - He reports improved validation, fewer imports, and a smoother migration experience. Transcript: Scott Tolinski I've just been really, really, as I get through with this, I'm writing less code, I'm getting better validation. The TypeScript stuff is so nice. I'm not having to import types and anything like that. All type safe across the board. It rules. Very big fan of this. So that's pretty much it. ([Time 0:25:01](https://share.snipd.com/snip/0212eae7-a610-4b54-9a36-7702d2ed6ef2)) - **Cache Remote Queries Locally** - Consider adding a client-side cache layer (service worker + IndexedDB) for remote queries to enable stale-while-revalidate behavior. - Scott built a 150-line service worker that caches remote query results and serves them instantly on client loads. Transcript: Scott Tolinski Yeah. So caching, especially like it would still happen at like if you're, especially if you're doing server side rendering, right? That's still happening at the page boundary, right? If you're caching that, it's still happening at the page boundary. I think there's going to be more with the caching of the data. There's some caching stuff with the pre-render queries using the cache API. But in general, on these functions, you're not setting cache headers. Wes Bos So this is something that only Next.js, I think, has nailed. And it's the ability to do a component-level cache. And a lot of people don't understand. So let me give an example. On my website, westboss.com, I have the syntax, latest syntax podcast in the footer. And that is a syntax component in the footer of my website. And that does a fetch to the syntax API and then brings it back. And there's a problem where, and I have a couple other things in the footer of every page, right? And I can, I cache the data there, right? But I kind of want to just cache the entire component as it's being rendered. And there's nobody except for Next.js that has the ability to cache a component level. Everybody's caching entire pages, which is sometimes okay. But what I end up with sometimes is that if I cache an entire page, then the footer data is sometimes different from page to page because they've been cached at different points. So the other option is you can simply just cache the expensive stuff which is the data fetching and then just re-render every single time or set like a really short re-render on it but I'm i'm curious to see and i bet svelte will roll out something like this as well where you'll be able to just like cache this entire component for however long and then do the whole stale Wall route of validate headers on it. And that would be sick, because then you can reuse that component anywhere else on your dynamic site, but you don't have to spend the overhead of re-rendering that sucker and doing data Fetching. Scott Tolinski Well, let me tell you, Wes, I also felt this pain. I felt this pain, and that's why I took it upon myself to make something. So I wrote, and I'll share this with everybody. And this is, I'll say this, it's naive, it's a quick implementation, but it works really well. What this is, is a service worker because service workers are built into SvelteKit. It's a service worker that basically does stale wall, revalidate using index DB. So it takes any, right now it's just queries, it takes any remote function query because I can identify those, it takes any remote function query and saves it to index DB on the next page Load. If it's being done on the client side, it's going to check index DB and just load that data. So it's basically like a local index DB cache that I don't even have to do anything in my code because it's a service worker. It's just there. It worked really well. ([Time 0:25:28](https://share.snipd.com/snip/f7b3d810-008c-46c6-8d91-a3fa36960043)) - **Component-Level Caching Is Hard** - Component-level caching remains a hard problem; SvelteKit currently caches at page boundaries while Next.js offers component-level cache controls. - Expect future improvements to component caching in meta frameworks to reduce repeated rendering and data fetching. Transcript: Scott Tolinski But in general, on these functions, you're not setting cache headers. Wes Bos So this is something that only Next.js, I think, has nailed. And it's the ability to do a component-level cache. And a lot of people don't understand. So let me give an example. On my website, westboss.com, I have the syntax, latest syntax podcast in the footer. And that is a syntax component in the footer of my website. And that does a fetch to the syntax API and then brings it back. And there's a problem where, and I have a couple other things in the footer of every page, right? And I can, I cache the data there, right? But I kind of want to just cache the entire component as it's being rendered. And there's nobody except for Next.js that has the ability to cache a component level. Everybody's caching entire pages, which is sometimes okay. But what I end up with sometimes is that if I cache an entire page, then the footer data is sometimes different from page to page because they've been cached at different points. So the other option is you can simply just cache the expensive stuff which is the data fetching and then just re-render every single time or set like a really short re-render on it but I'm i'm curious to see and i bet svelte will roll out something like this as well where you'll be able to just like cache this entire component for however long and then do the whole stale Wall route of validate headers on it. And that would be sick, because then you can reuse that component anywhere else on your dynamic site, but you don't have to spend the overhead of re-rendering that sucker and doing data Fetching. ([Time 0:25:50](https://share.snipd.com/snip/9ae4ef45-9cdf-4e6e-a29f-d4f5010aa207))