Remote Functions with Simon Holthausen

Speaker 1: Hello, everyone. Welcome to another episode of Svelte Radio. I'm here today with Simon

Speaker 1: from the Svelte team.

Speaker 1: Hello.

Speaker 1: Hey, welcome for having me.

Speaker 1: Thank you

Speaker 2: for having me.

Speaker 2: Not welcome.

Speaker 3: Great intro.

Speaker 2: Stumbling upon

Speaker 3: my words right

Speaker 2: away.

Speaker 1: When it's digital,

Speaker 1: am I visiting you

Speaker 1: or are you visiting me?

Speaker 1: You know?

Speaker 3: I'd say I

Speaker 1: visit you.

Speaker 1: Oh, okay.

Speaker 1: I was trying to save you here

Speaker 1: with the welcome.

Speaker 3: Yeah.

Speaker 1: Yeah, it's beyond repair.

Speaker 1: Yeah.

Speaker 1: All right, all right.

Speaker 1: So before we start, Anthony and Brittany were supposed to be here, but we ran into a couple of scheduling issues.

Speaker 1: And I decided I'm just going to do this on my own because it's easier.

Speaker 1: And we want to get this out there because it's really interesting and fun.

Speaker 1: The stuff that we're going to talk about today.

Speaker 1: Remote functions, async, svelte, I guess that's what you would call it?

Speaker 1: Yes.

Speaker 1: Kind of two parts, yeah.

Speaker 1: So maybe start off with what are remote functions in SvelteKit?

Speaker 1: So remote functions

Speaker 2: are a new way to interacting with data in your SvelteKit applications.

Speaker 2: Today, you have load functions and form actions to do that.

Speaker 2: So load functions you have in your plus page TS or plus page.server TS file,

Speaker 2: export a load function and you do things in there.

Speaker 2: You can also have layout load functions.

Speaker 2: And that's how you can get your data.

Speaker 2: And if you want to interact with that data, not just read it, but also manipulate it,

Speaker 2: the first class way to do that is forum actions, which live in plus page.server.ts files.

Speaker 2: And basically, it's a nice way of writing regular forum posts,

Speaker 2: just like basic web form posts.

Speaker 2: And this has served us very well so far.

Speaker 2: But over the years, a few things have become apparent

Speaker 2: that it just doesn't like scale as much as

Speaker 1: you'd like,

Speaker 2: like both scale up, but also scale down.

Speaker 2: I know.

Speaker 2: I know one--

Speaker 1: sorry, I didn't mean to cut you off.

Speaker 1: But I know one thing I've always gotten a bit irritated

Speaker 1: is that

Speaker 3: when you have

Speaker 1: one page

Speaker 1: and you have a bunch of different kinds of data

Speaker 1: that you want to bring into the page

on first load,

but then you might want to refresh just part of the data.

At the moment, with load functions,

you have to rerun the whole thing, right?

And then you would fetch everything from...

Correct.

That's one of the drawbacks

Speaker 2: that you can only get as granular with loading

Speaker 2: or reloading as a load function.

Speaker 2: So I don't know if you want to only reload a certain thing,

Speaker 2: then maybe you, I don't know, need to introduce a layout

Speaker 2: that is UI

Speaker 3: -wise empty

Speaker 2: just to split it apart.

Speaker 2: And that's like really clunky.

Speaker 2: So yeah, you're

Speaker 3: forced to...

Speaker 3: You can use groups,

Speaker 2: right?

Speaker 2: You could use

Speaker 1: groups.

Speaker 3: Nested groups.

Speaker 2: Yes.

Speaker 2: And yeah, so there are ways around it with enough like painkillers besides you.

Speaker 2: But yeah, it's not good.

Speaker 2: It also, another problem with loading data like that is that the co-location is not great.

Speaker 2: So you're loading your data in all these load functions.

Speaker 2: And then maybe you're using part of that data only very deep inside your page, like, I don't know, five components deep.

Speaker 2: And then you

Speaker 3: have either

Speaker 2: the choice to do prop drilling or go with the page store, which basically is a collection of all the data on that page.

Speaker 2: But that is no longer really type safe.

Speaker 2: So, yeah, that's another problem here.

Speaker 2: And also keeping those things in sync isn't as nice

Speaker 2: because maybe you want to refactor something

Speaker 2: or delete that data,

Speaker 2: but then you have to always think about the other end

Speaker 2: where you also need to change or delete your code.

Speaker 2: And speaking of TypeScript,

Speaker 2: the same is true for forum actions.

Speaker 2: So they work really nicely the way you author them,

Speaker 2: but it's not exactly type safe

Speaker 2: because the way you do it is you write basically the action path for the form.

Speaker 2: You write yourself and then you have to be sure that it's in sync

Speaker 2: with whatever you have in your plus page.server.ts file.

Speaker 2: And we have also heard people saying like,

Speaker 2: hey, I have this in my plus page.server.ts file,

Speaker 2: but maybe I want to use this on several pages.

Speaker 2: And so how can I do this?

Speaker 2: And so there are multiple, basically, problems to that too.

Speaker 2: And so also, what if you don't want to use form actions?

Speaker 2: So, I mean, we, of course, advocate for progressive enhancement,

Speaker 2: which means everything ideally should also work without JavaScript.

Speaker 2: But maybe you're doing some internal business app

Speaker 2: or the thing you're interacting with requires JavaScript?

Speaker 2: And then why would you want to use forms in that case?

Speaker 2: And SvelteKit basically offers you nothing in that regard.

Speaker 2: So you're completely on your own, just using

Speaker 1: regular fetch.

Speaker 1: And so I've had to use, I mean, I would use,

Speaker 1: so I always use SuperForms to do forms in SvelteKit before.

Speaker 3: And that

Speaker 1: kind of works in the SPA way as well.

Speaker 1: But it's still, like, it's a bit weird.

Speaker 1: You're kind of, what's it called?

Speaker 1: Faking forms in a way.

Speaker 1: Because you're using super forms and it kind of looks like you're using form actions, but you're not really.

Speaker 1: Because it's all SPA networks.

Speaker 1: But it would be better if it was easier, better DX.

Speaker 2: Right. And so we were thinking, okay, how can we basically keep the niceness about data loading and make it more granular, more type safe, give a better first class integration with when you don't want to use forms and so on.

Speaker 2: And this is how remote functions came to be.

Speaker 2: And it all started back in May when we had our offsite, basically.

Speaker 2: So prior to the Svelte Summit, many of the maintainers came together and we did a brainstorming.

Speaker 2: And previously, we already had written up some discussions.

Speaker 2: But yeah, the bulk of the design happened in those two to three days prior to the conference.

Speaker 2: like it's it's amazing what you can do when when you get people in a room yeah and and talk to each

Speaker 2: other live so i mean i'm a proponent of remote

Speaker 1: uh first i was gonna say the exact same thing

Speaker 2: yeah right like remote first

Speaker 1: is nice but it's hard things like this it's

Speaker 2: hard to beat in person

Speaker 2: right especially when you consider that many of us are not working on this full-time and to have

Speaker 2: these voices in the same room still and not having to wait on them like I don't know four days until

Speaker 2: they give a response that that is so valuable and so yeah we we got out with remote functions and

Speaker 2: that is basically I would say our take on RPC so it looks like you write regular functions and call

Speaker 2: regular functions and on the server that is what you are doing but on the client you're actually

Speaker 2: doing a fetch call to the server and the server knows how to basically deal with this special call

Speaker 2: call the actual function and then come back to the client where it can unpack the serialized

Speaker 2: response. And more specifically, you write your remote functions in.remote.ts or.remote.js

Speaker 2: files. So for example, I don't know, if you have a blog post and then maybe you do like a

Speaker 2: blog.remote.ts file. And in there you can then have a so-called query function,

Speaker 2: query remote function. So you import all these functions from $app.server.

Speaker 2: and for get posts you would do a query and in there you would query your database and return

Speaker 2: the list of posts when you are creating a new blog posts you would use the form function which

Speaker 2: yeah you give a schema standard schema like zod or valley bot and then that parses the form

Speaker 2: gives you back an object from that and then you can interact with that and i don't know do a few

Speaker 2: checks like are you authenticated are you authorized to do a blog post to create a new entry and so on

Speaker 2: and then call the database.

Speaker 2: And you can even right then and there

Speaker 2: do something like a single flight mutation

Speaker 2: where you're saying,

Speaker 2: okay, I want to refresh get posts.

Speaker 2: So as part of the response,

Speaker 2: you're also already getting the new data.

Speaker 2: And you could maybe also have a toggle like in there.

Speaker 2: So as a reader, you could press the like button

Speaker 2: and maybe that is for some reason

Speaker 2: not a form under the hood, but it's a so-called command.

Speaker 2: So that's the third one you have.

Speaker 2: And the command works very similar to the form.

Speaker 2: You also have a schema if you have arguments.

Speaker 2: In case of toggle like, you probably don't have arguments and so on.

Speaker 2: And so basically you have all these different functions,

Speaker 2: which all are called remote functions,

Speaker 2: and you have them inside this.remote.ts file.

Speaker 2: And then in your plus page, Svelte or any other Svelte file or regular file,

Speaker 2: you can just, you just can interact with them like regular functions, which return promises.

Speaker 1: So things

Speaker 2: that

Speaker 1: come out of this is like, we get full type safety, right?

Speaker 1: Yes.

Speaker 1: Without any magical kind of things that, because before we had, you had to do some

Speaker 1: magic under the hood, right, for the load functions,

Speaker 1: or am I misremembering?

Speaker 2: Yes, so, right.

Speaker 2: So one of the,

Speaker 2: that's another thing that gets better here.

Speaker 2: So for load functions,

Speaker 2: to make them type safe,

Speaker 2: we had to do a bit of TypeScript voodoo.

Speaker 2: Like if you do let brackets data equals dollar props,

Speaker 2: Like there's zero types, type safety.

Speaker 2: Yeah.

Speaker 2: So you don't write the types yourself.

Speaker 2: It's done in the background for you by the language service.

Speaker 2: You could also write it explicitly by importing page props from a relative file called dollar types, which doesn't

Speaker 3: exist.

Speaker 3: And

Speaker 2: at this position, it actually exists somewhere in the generated files, which is another TypeScript trick which you can employ to make that

Speaker 3: work.

Speaker 2: And it works and it's nice, it's clever, but wouldn't it be better if we didn't have to do this?

Speaker 2: Because

Speaker 3: it's

Speaker 2: something we have to maintain.

Speaker 2: It may break down in some places and so on.

Speaker 2: And if we can just rely on regular TypeScript, so to speak, that would be even better.

Speaker 2: And yeah, with remote functions, from a TypeScript perspective, it's just that.

Speaker 2: It's just TypeScript functions.

Speaker 2: And so that's why TypeScript will have a much easier time with this and why type safety will

Speaker 2: be increased with this with less effort.

Speaker 2: Because it's not just that your load functions will have type safety.

Speaker 2: Also, your forms now will have type safety.

Speaker 2: And your commands will also have type safety.

Speaker 2: It's not actually functions, though, and that's because, like, on the client, you're actually doing a fetch call to the back end.

Speaker 2: And that's why we encourage people to use a schema

Speaker 2: whenever they require arguments for the commands or queries or forms.

Speaker 2: Because in theory, everyone could call this endpoint with rubbish

Speaker 2: and could try to break it.

Speaker 2: And

Speaker 1: so that's why schema is required.

Speaker 1: Yeah, they're not hidden or anything.

Speaker 2: Right.

Speaker 2: It's auto-generated and it has funky names.

Speaker 2: So it's not exactly predictable or anything, but it's still public after all.

Speaker 2: And so you have to be careful there.

Speaker 2: And that's why we encourage you

Speaker 1: to do this.

Speaker 1: So speaking of schemas, then, it's a bit interesting that we ended up, because I have some vague memory of the Svelte core maintainers not wanting to deal with validation in any sense, because it's like an extra thing to have to do.

Speaker 1: But I think since then, there's this, what's it called, standard schema that's come out from all of these validation libraries.

Speaker 3: So these remote

Speaker 1: functions, they have support for passing in, well, support for, you have to pass in something if you're taking a parameter, right? You have to pass in a schema.

Speaker 1: You

Speaker 2: could also do a quote as a string unchecked.

Speaker 2: That way you can bypass it.

Speaker 2: Like if you know what you're doing or you're using some very custom built validation library

Speaker 2: that is not standard schema compliant, then you can bypass it.

Speaker 2: But the name unchecked should already clue you in that this goes unchecked.

Speaker 2: Like the types say this is a string, but it's not necessarily a string.

Speaker 2: And so that's where the encouragement to use it comes in,

Speaker 2: but

Speaker 1: we don't force you to do it.

Speaker 1: So what does it look like?

Speaker 1: Let's say I have a blog and I want to fetch one blog post.

Speaker 1: So I do const get post equals query.

Speaker 1: And then what do I do to get the

Speaker 3: post?

Speaker 3: Yeah, if you're

Speaker 2: using ValleyBot, for example,

Speaker 2: then you would do v.string as the first argument,

Speaker 2: which is the first argument is the schema

Speaker 2: or the string unchecked.

Speaker 2: And you do v.string.

Speaker 2: And then the second argument is the function

Speaker 2: that is then called.

Speaker 3: And

Speaker 2: in that case, it's, I don't know, probably the ID.

Speaker 2: And

Speaker 3: ID would

Speaker 2: already be type checked in that sense.

Speaker 2: So you don't have to do like colon string yourself anymore.

Speaker 2: That's inferred

Speaker 3: from the schema.

Speaker 3: So in a sense,

Speaker 2: most of the time you're actually not typing anything more

Speaker 2: compared to not using a schema, which is kind of nice.

Speaker 2: Yeah, and then in there you would, I don't know, query

Speaker 3: your database,

Speaker 2: fetch fetch the blog post from wherever and return it in a way that svelte kit can

Speaker 2: then serialize it you serialize it and so on so as long as you use something that is

Speaker 2: devalue compliant then it works so like everything about json is okay dates are okay map is okay

Speaker 2: and you can even

Speaker 2: so SvelteKit has this transport

Speaker 2: transport hook

Speaker 2: where you can basically define

Speaker 2: custom classes

Speaker 2: and how they should be

Speaker 2: serialized, deserialized. You could even use that

Speaker 2: in there.

Speaker 2: I was going

Speaker 1: to ask about

Speaker 1: the types of data that you can

Speaker 1: send back and forth. I assume you can't

Speaker 1: do functions.

Speaker 1: Correct. You could do

Speaker 1: functions. You can do

Speaker 1: functions? No, you couldn't.

Speaker 1: okay I

Speaker 2: mean you could maybe you could have a special class that represents a function or

Speaker 2: in some

Speaker 3: way and then you

Speaker 2: have knowledge domain knowledge about how this should be deserialized

Speaker 2: and serialized and then

Speaker 1: but not like a generic function yeah but

Speaker 2: no generic functions so

Speaker 1: that

Speaker 1: I've tried the

Speaker 1: query functions a bit

Speaker 1: and I find that

Speaker 1: having to

Speaker 1: use a schema for

Speaker 1: my queries also makes

Speaker 1: it kind of encourages using

Speaker 1: schemas in the rest of the application

Speaker 1: as well, which is kind of nice.

Speaker 1: So that's another side effect, I guess,

Speaker 1: of remote functions,

Speaker 1: if you will, for me at least.

Speaker 2: Yeah,

Speaker 2: I think so too, that in general

Speaker 2: will encourage better practices

Speaker 2: and ultimately make your app more resilient.

Speaker 1: Yeah.

Speaker 1: Okay, so remote functions do rely on this other feature

Speaker 1: that was built for Svelte called,

Speaker 1: I don't know if you call it async Svelte or async?

Speaker 1: Yeah, async.

Speaker 3: Top level await.

Speaker 1: Async Svelte, okay, yeah.

Speaker 1: But maybe we can talk a bit about that.

Speaker 1: It was introduced, was it introduced at Svelte Summit or a bit before?

Speaker 1: And then Rich did a talk about, I don't remember.

Speaker 2: Yeah, it was introduced at Svelte Summit.

Speaker 2: That's correct.

Speaker 2: And remote functions also got like an outlook there.

Speaker 2: And yeah, so async Svelte, for those who don't know yet,

Speaker 2: basically for the longest time, people have asked,

Speaker 2: I want to use the await keyword in my components,

Speaker 2: preferably at the top level, but maybe also somewhere else.

Speaker 2: And so far that didn't work.

Speaker 2: The only way you could do that is either by using the await block,

Speaker 2: the curly braces hash await.

Speaker 2: And that's a bit like clumsy because it only works

Speaker 3: for

Speaker 2: one promise at a time.

Speaker 2: so you have multiple then you got to repeat this again and again and again and each of them are

Speaker 2: basically in their own life cycle of showing a loading screen and then showing the data and if

Speaker 2: you have i don't know 10 of this uh these on your page and it's like 10 loading spinners and it's

Speaker 2: like it makes for a horrible experience

Speaker 3: so so

Speaker 2: you want to coordinate that somehow ideally the other

Speaker 2: way you could work around it is by flattening the async into something synchronous by having like a

Speaker 2: resource like object where you have loading and current and error properties on an object and

Speaker 2: then you could use that but that also like it could be clumsy sometimes and could make it so that

Speaker 2: the coordination problem is still there like ideally you want to somehow have maybe define

Speaker 2: okay this is my boundary basically and that's where I want the one loading screen to appear

Speaker 2: and then once that's done I like I want to show that loading screen until all async functions

Speaker 2: are resolved and then go from showing that to showing the the end result and not have 10

Speaker 2: spinners i only have one spin spinner so to speak and that's what async spelled basically gives you

Speaker 2: it gives you the ability to use a weight inside the template and at the top level you can also

Speaker 2: use it inside derived so basically like asynchronous derivations that then also works

Speaker 2: um and you can wrap these in a boundary and give that boundary a pending snippet which then shows

Speaker 2: up on first render for as long as anything inside the boundary is still loading and that that doesn't

Speaker 2: have to be in the same component it could be 10 components deep so it's a runtime concept like the

Speaker 2: runtime knows where the nearest boundary is and what async work is still out pending and yeah and

Speaker 2: that that way you you can coordinate it so

Speaker 1: you mentioned uh you mentioned the word resources

Speaker 1: I've heard this word kind of in some places.

Speaker 1: Is that something special?

Speaker 3: No, I think it's just like a term

Speaker 1: that is

Speaker 2: more or less common in the async.

Speaker 2: When talking about async, you're talking about resources,

Speaker 2: or at least we in the team are calling it resources.

Speaker 2: Okay.

Speaker 2: In fact, we, so with remote functions, especially with queries and so on, like they give you nice ergonomics and people have rightfully asked us, hey, this requires a server because like the remote function lives on the server.

Speaker 2: So you have to have a server runtime.

Speaker 2: What about the SPA case?

Speaker 2: What if I want to use something like a query there too?

Speaker 2: And we are actually in the process of designing and implementing a resource-like API right now, which will bring the query-like capabilities to Svelte itself.

Speaker 2: And the client implementation of remote function queries will then likely use that under the hood.

Speaker 1: So you would kind of migrate the current remote functions

Speaker 1: to use that when

Speaker 2: the resource API is

Speaker 1: done.

Speaker 1: Right.

Speaker 1: Yeah, it's all connected.

Speaker 1: Yeah.

Speaker 1: Yeah.

Speaker 1: That's kind of nice to hear that the SPA use case

Speaker 1: is getting some love

Speaker 1: because it's been a bit neglected, I feel like,

Speaker 1: in SvelteKit, at least.

Speaker 2: Yeah.

Speaker 2: Like you've always been able to use Svelte,

Speaker 1: right?

Speaker 2: That is true.

Speaker 2: Like, I...

Speaker 2: it felt a bit neglected

Speaker 2: and I appreciate that feedback

Speaker 2: and I get where it comes from.

Speaker 2: And I think with Async Svelte itself already,

Speaker 2: the SPA case already is easier,

Speaker 2: much easier to do

Speaker 2: because if you wanted to have proper data loading,

Speaker 2: a proper data loading story,

Speaker 2: you basically had to use SvelteKit.

Speaker 2: But now

Speaker 3: with async

Speaker 2: Svelte, where you can just slap in a weight in front of a fetch, for example, coordinating asynchronous work in SPA only applications, even applications that don't use SvelteKit, they just use Svelte.

Speaker 2: That's now much easier and much more doable compared to before.

Speaker 1: Yeah.

Speaker 2: We

Speaker 1: haven't had enough

Speaker 2: documentation on this topic though yet.

Speaker 2: So that's another part.

Speaker 2: The feeling comes from that many of our documentation is SSR and progressive enhancement centric.

Speaker 2: And I think we can do better here with having more for SPA use cases without like abandoning the progressive enhancement.

Speaker 1: yeah because that's an important part

Speaker 1: like the progressive enhancement and the

Speaker 1: kind of building for

Speaker 1: people that aren't necessarily

Speaker 1: using JavaScript in their

Speaker 1: browser

Speaker 2: or don't get it fast enough

Speaker 2: yeah

Speaker 1: for any reason that

Speaker 2: they don't have

Speaker 1: JavaScript

Speaker 1: working on the website on the page at the moment

Speaker 1: okay

Speaker 1: cool so

Speaker 1: that was ASIC Svelte

Speaker 1: there's talk

Speaker 1: and link. We talked a bit about queries. Anything else about queries that we should talk about? I

Speaker 1: know there's a query.batch where you can kind of fetch many things and then... Yeah, so now that we

Speaker 1: have this remote

Speaker 2: functions primitive, it's very fun to think about all the additional things we

Speaker 2: can bring to it on top.

Speaker 2: So

Speaker 2: query batch is

Speaker 2: one example.

Speaker 2: Just to quickly

Speaker 2: explain what query batch does, it's

Speaker 2: solving the so-called n

Speaker 2: plus one problem. So

Speaker 2: imagine you have a list of

Speaker 2: cities and for each city you want

Speaker 2: to get the

Speaker 2: weather data for that city.

Speaker 2: And

Speaker 2: I mean, you could have

Speaker 2: like a

Speaker 2: an endpoint which says okay let's get me the weather for all these cities and you could write

Speaker 2: it yourself but i don't know maybe the list changes and then you basically you you re-request

Speaker 2: the data for all the ones you already have and not just the i don't know five that are new or

Speaker 2: something so instead ideally what you want is to call on the client just get weather as a query

Speaker 2: for each of the cities but that means that you would in the worst case do um i don't know let's

Speaker 2: say it's a list of 20 and then you do 20 uh query requests and do therefore do query 10

Speaker 2: at 20 back-end requests and maybe each with i don't know in the case of whether it's probably

Speaker 2: easy but i don't know maybe this is behind some authentication and then for each thing you're

Speaker 2: doing the authentication dance doing some database request the database request maybe itself is

Speaker 2: already pretty complex and therefore taxing on the database and so on and you're doing that 20 times

Speaker 2: And instead, what you could do is basically do what the alternative is to do it, to do manually to bulk request 20 at one.

Speaker 2: But from the client, it looks like you're doing like 20 requests and query batch then batches them into one request.

Speaker 2: So on the client, you're calling this with one city each.

Speaker 2: And on the backend, you receive an array of cities.

Speaker 2: So you only do one request, only do one response.

Speaker 2: And on the client, it can then spread it out again to the right requests.

Speaker 2: And that's what query batch does.

Speaker 1: So in my example of getting a blog post, this would be get the list of blog posts for the front page.

Speaker 1: Basically, you would use query batch there, I assume.

Speaker 2: If you want to have the details of all the blog posts, then yes, you could do that.

Speaker 2: I guess for the front page, I would probably still do one get posts, which has a different structure.

Speaker 2: like it's only containing

Speaker 3: some short

Speaker 2: summary

Speaker 2: and the title or something

Speaker 2: but yeah

Speaker 2: like it's not common

Speaker 2: but when you run into this

Speaker 2: it's kind of

Speaker 2: annoying and

Speaker 2: now with remote functions

Speaker 2: with this nice

Speaker 2: primitive we have

Speaker 2: the ability to also

Speaker 2: provide more quality

Speaker 2: of life functions

Speaker 2: that way. And if you don't use it, it's basically

Speaker 2: it's tree shakable. So if you don't use that, then you're also not paying for this.

Speaker 2: That's the best scenario.

Speaker 1: Getting a lot of functionality.

Speaker 2: Right. There's going to be others.

Speaker 2: Like we have to think about what we want

Speaker 2: to do with caching. This is an ongoing design

Speaker 2: question. Ideally, it's

Speaker 2: more than just set headers

Speaker 2: because

Speaker 2: setting headers and getting that right

Speaker 2: that's like really

Speaker 2: it's almost rocket science

Speaker 2: like setting

Speaker 2: the correct cache headers in the correct ways

Speaker 2: that's not easy

Speaker 2: and like many

Speaker 3: cloud providers

Speaker 2: have now

Speaker 2: more elaborate

Speaker 2: ways of doing caching and

Speaker 2: ideally we can on a provider basis

Speaker 2: integrate with that

Speaker 2: but having an

Speaker 3: API that basically

Speaker 2: makes this write ISR for example.

Speaker 2: So that's one thing we're talking about.

Speaker 2: We're talking about query.stream

Speaker 2: which basically allows you to stream data

Speaker 2: live data from the backend to the frontend.

Speaker 2: There's people who want this both ways

Speaker 2: in both directions.

Speaker 2: So something around WebSockets

Speaker 2: We have to think about that, see how that goes.

Speaker 2: So yeah, there's lots of possibilities

Speaker 2: and it's all going to revolve around the same primitives

Speaker 2: and the same ideas and APIs.

Speaker 2: And so I feel like it's going to become a really nice set of APIs,

Speaker 2: which will give you a good way for many of your use cases

Speaker 2: without restricting you or something

Speaker 2: and also without forcing you to use it all at once.

Speaker 2: You can explore over time and grow with it.

Speaker 1: Yes,

Speaker 2: I've

Speaker 1: tried it out for a bit

Speaker 1: in preparation of this interview

Speaker 1: and I experimented with moving.

Speaker 3: I've only had

Speaker 1: time to try out the query one,

Speaker 1: but just converting a load function into a query

Speaker 1: remote function super easy. I

Speaker 3: really

Speaker 1: enjoyed it. Like it's very nice. And you also get that like

Speaker 1: one thing I ran into, because I stupidly didn't read the documentation that well, was the like,

Speaker 1: how do I get access to locals? And then I discovered, what do you call it? Like get request

Speaker 1: event for this, which is pretty nice. And it's what I discovered as well. I didn't realize this

Speaker 1: at first, but you can use getRequestEvent

Speaker 1: in a function that you're using inside of a remote

Speaker 1: function. So at first I thought

Speaker 1: I would chain

Speaker 1: I would call a remote function inside of another remote function

Speaker 1: but then I realized why would I do that when I can just call

Speaker 1: a regular function? For some reason I didn't realize

Speaker 1: that I could do that.

Speaker 2: Yeah, that's definitely another beauty of this.

Speaker 2: It's all functions and it looks like functions,

Speaker 2: but it actually is just functions without any gotchas.

Speaker 2: So you could use other queries from within queries.

Speaker 2: You could use regular functions from within queries.

Speaker 2: So yeah, you can compose and nest this as you like,

Speaker 2: Which is really nice.

Speaker 2: And at

Speaker 3: the same time,

Speaker 2: because it's so like,

Speaker 2: because the boundary is the file,

Speaker 2: you don't

Speaker 3: run into

Speaker 2: any weird hiccups where, I don't know,

Speaker 2: you accidentally close over a variable

Speaker 2: and then somehow end up having a security leak.

Speaker 2: That's one of

Speaker 3: the reasons why

Speaker 2: we went with file boundaries

Speaker 2: compared to use server, for example.

Speaker 1: Ah, right, like in React, I guess.

Speaker 1: I have no idea how use server and stuff works in React.

Speaker 1: So, all right.

Speaker 1: So the experience is nice.

Speaker 1: I really enjoyed it.

Speaker 1: I haven't tried the form stuff yet,

Speaker 1: but I am sure I'll have a great time.

Speaker 1: But there's also like,

Speaker 1: I've heard that you're not quite done

Speaker 1: with how the form stuff works yet?

Speaker 1: Like you're evaluating, maybe changing it a bit.

Speaker 1: Is that right?

Speaker 1: Or am I misremembering maybe?

Speaker 2: Yeah, so there's a couple of ongoing tweaks right now.

Speaker 3: So we

Speaker 2: started out with form functions

Speaker 2: retrieving the regular form data,

Speaker 2: and then you have to like pull out the data yourself from there.

Speaker 2: We since then have switched it to requiring a schema

Speaker 2: as the first argument and then doing conversion logic there

Speaker 2: so that you get a regular pojo from form data,

Speaker 2: which is already much nicer.

Speaker 2: and the way you interact with the form on the client

Speaker 2: when, for example, you have, I don't know,

Speaker 2: you return a list of issues via the schema

Speaker 2: and then you can show that in the client.

Speaker 2: You can show the current value on the client.

Speaker 2: And the way this works is still a bit rough

Speaker 2: and we're in the works of tweaking this

Speaker 2: to give a much nicer API.

Speaker 2: So it's probably going to be something like this

Speaker 2: that you have a fields property on your form.

Speaker 2: And from there on, you just use regular.notation

Speaker 2: to go wherever you want to go in your model.

Speaker 2: And it's type safe because through the schema,

Speaker 2: it knows which things are available at which level.

Speaker 2: so in other words you don't have to have flat forms like it your your object can be nested

Speaker 2: you don't have to have it only one level deep yeah and through the stop mutation and then you can get

Speaker 2: a much easier time connecting that to your input so that things are in sync with the value and so

Speaker 2: on and yeah it's it's it's a bit hard to explain just

Speaker 1: over

Speaker 3: voice but yeah for sure yeah

Speaker 2: just just

Speaker 2: stay tuned it's it's gonna be out soon

Speaker 3: and um yeah

Speaker 2: it will make forms uh even easier to work with

Speaker 2: um yeah yeah so

Speaker 1: uh some questions there um with with regards to forms like form handling forms

Speaker 1: from end to end is like a huge undertaking, right?

Speaker 1: If

Speaker 3: you

Speaker 1: look at super forms, it's super complicated

Speaker 1: because there are so many edge cases

Speaker 1: and so much functionality that people want.

Speaker 1: How far do you think you guys will go with implement?

Speaker 1: Because validation is the first step, right?

Speaker 1: Because then you have to handle error messages.

Speaker 1: How do you display the error?

Speaker 1: Like where do you get the error messages from?

Speaker 1: Where do you, how do you handle like if the form has been touched, et cetera, et cetera?

Speaker 1: Like there's just like a, like

Speaker 2: a rabbit hole of stuff that you

Speaker 3: can end up implementing.

Speaker 2: Yeah, I mean, we started out without having a way to get issues because we didn't have schema yet.

Speaker 2: Now that we have a schema, through the schema, you can tell when a field fails,

Speaker 2: you can basically return a message along with it that is then sent back to the client.

Speaker 2: You can also now have so-called pre-flight, which is basically validation on the client as you type.

Speaker 2: So yeah, we have added more stuff because the community said like,

Speaker 2: oh yeah this is nice but do you know what would be even nicer if you

Speaker 1: also had

Speaker 2: this

Speaker 1: yeah just i mean just just opening the super forms documentation

Speaker 2: is basically like that

Speaker 1: that's how you

Speaker 1: end up with super forms or like all the fresh now because you kind of need that the

Speaker 2: goal so the

Speaker 2: the goal will explicitly not be to rebuild super forms into

Speaker 3: svelte

Speaker 2: kit so there will still be room

Speaker 2: for super form to to exist but the the goal basically is to solve the 80 percent use case

Speaker 2: and leave the rest of the 20 percent which are the ones where like you get increasingly

Speaker 2: diminishing returns

Speaker 3: yeah with

Speaker 2: even more edge cases and so on and to leave those

Speaker 2: out in favor of a consistent, concise API that is easy to use, to understand, but at the same time

Speaker 2: leave enough room for people to build something on top of it. So you could imagine a world in which

Speaker 2: maybe at some point SuperForms is built as an extension on top of remote form functions.

Speaker 1: That makes sense. I mean, the complexity would just like skyrocket if you would add all of the functionality and features that you would need from, if you wanted to build a one-to-one kind of thing, proper full featured form library, if that makes sense.

Speaker 2: That's correct.

Speaker 1: But that sounds like a good, like, building for 80% of the use cases sounds good.

Speaker 1: Anything else about forms that we should talk about?

Speaker 1: That nothing springs to mind right now.

Speaker 2: Just that, like, you can still redirect from there.

Speaker 2: You can still throw an arrow in there.

Speaker 2: So basically all the existing ways in which you used forms so far, they still continue to exist.

Speaker 1: - Okay, cool.

Speaker 1: So let's say not having used the form remote function yet.

Speaker 1: What if I, after posting some data to,

Speaker 1: let's say it's a to-do list and I'm,

Speaker 1: I want to post a new to-do,

Speaker 1: and then I want to refresh the data that I have on,

Speaker 1: on the, from my query that,

Speaker 1: that I use to fetch the to-dos, right?

Speaker 1: So let's say I have a query,

Speaker 1: a query remote function that's called get to-dos.

Speaker 1: And then I have a create to-do form remote function.

Speaker 1: what would be like an easy way to refresh the data?

Speaker 1: Because in the old SvelteKit way,

Speaker 1: you would just form, you would submit the form

Speaker 1: and then the load function would rerun.

Speaker 1: So how does it work now?

Speaker 2: So by default, it will refresh everything on the

Speaker 1: page

Speaker 1: to mirror the...

Speaker 1: So just like before.

Speaker 2: Just like before to mirror the non-progressive

Speaker 2: and the Hampton page case,

Speaker 2: in which case you would basically get a full page reload,

Speaker 2: which basically means you reload everything.

Speaker 1: So does it, how does, yeah.

Speaker 1: Sorry, no, I was going to say,

Speaker 1: like, how does it, does it just know,

Speaker 1: like, exactly what remote or query remote functions

Speaker 1: that are on the page?

Speaker 1: That's correct.

Speaker 1: So there's

Speaker 2: a, basically there's a hidden client cache

Speaker 2: that knows about all queries that are on the page.

Speaker 2: That also means that if you are using,

Speaker 2: I don't know, get user,

Speaker 2: as a query in three different places.

Speaker 2: You're not actually doing three different fetches.

Speaker 2: You're sharing

Speaker 3: the

Speaker 2: same instance under the hood.

Speaker 2: So it's all shared, cached, client under the hood,

Speaker 2: which is another nice thing

Speaker 2: because you don't have to worry about like,

Speaker 2: I don't know, hoisting your data loading up, for example.

Speaker 2: Oh, I need get user, the user data.

Speaker 2: I need that in my layout as well now.

Speaker 2: So today you wouldn't hoist your

Speaker 3: getUserFetcher

Speaker 2: up into the layout function.

Speaker 2: And with remote functions,

Speaker 2: you just do like await getUser right then and there,

Speaker 2: and that's it.

Speaker 2: And you don't, yeah, as I said,

Speaker 2: you're not doing an extra request that way.

Speaker 2: It's just, it's deduplicated.

Speaker 2: It

Speaker 1: knows about that.

Speaker 1: So if I have a query function that's called getPost,

Speaker 1: and then I want to show two posts,

Speaker 1: As long as I assume, like, if I supply two different query parameters, it would run the

Speaker 1: query twice?

Speaker 1: Yes, so the cache

Speaker 2: key is basically the ID of the remote function plus the stringified

Speaker 2: payload.

Speaker 2: Okay, cool.

Speaker 2: And that way, we know about all the existing query functions on the page.

Speaker 2: And that means when a remote form function runs, it knows which things to refresh.

Speaker 2: And by default, it will refresh everything.

Speaker 2: But you can also opt into more granular refreshes by doing a so-called single flight mutation,

Speaker 2: which means you are not only returning data or doing the form post,

Speaker 2: you're also telling the client,

Speaker 2: hey, and these are the things that I wanted to have refreshed

Speaker 2: and this is the new data from those queries.

Speaker 2: So basically, in your form function,

Speaker 2: if you do like, I don't know, create post,

Speaker 2: you would then at the end of your inside your form function,

Speaker 2: would do something like get posts dot refresh

Speaker 1: and then get posts is the query remote function

Speaker 1: that exists so right right

Speaker 2: so you invoke the query function call dot refresh on it

Speaker 2: and that way the remote function knows okay the user is interested in getting the new data now

Speaker 2: of this thing so i'm going to request the get posts now and i'm going going to wait on its

Speaker 2: result and once it's there i'm going to put it into basically a hidden field on the return

Speaker 2: um on the response and then the client knows ah okay there's this hidden field and these are the

Speaker 2: hash keys i need to update and it's going to do that and that way you get both a more granular

Speaker 2: refresh because you're no longer refreshing anything everything but only this one and at

Speaker 2: the same time you get a single flight mutation which means you're faster because you're doing

Speaker 2: the mutation plus the refresh at the same time instead of having to do one round trip to the

Speaker 2: server to get the response uh like okay the form has succeeded to post there and then do another

Speaker 2: round trip to the server to say okay and now give me the refreshed data you're doing it in one

Speaker 2: single flight

Speaker 1: invitation yeah well that i mean that's because then you reduce like uh what's it

Speaker 1: called uh i forget what it's called like when you have to go multiple times

Speaker 2: multiple round trips yeah

Speaker 2: Yeah, the round trips, right?

Speaker 1: Yeah.

Speaker 1: So this brings up another interesting question that I have.

Speaker 1: Like, are you batching all of the queries into one request

Speaker 1: or are they done one at a time?

Speaker 1: This is actually an

Speaker 2: open question still,

Speaker 2: if we do that or not.

Speaker 2: It really depends.

Speaker 2: And that's also, I guess this is a,

Speaker 2: Please give us feedback which way you'd like us to go or rather.

Speaker 2: Give us feedback on how many requests you're having on your page.

Speaker 2: Like, do you have a few requests at once or do you have like,

Speaker 3: I don't

Speaker 2: know,

Speaker 2: 30 requests going on in your network tab at the same time you're starting to get a bit worried.

Speaker 2: We have it still open as a design question because

So what we're going to do is either have a way to specifically batch things across queries or have a way to say explicitly, I don't want to have

Speaker 3: this be

Speaker 2: automatically batched because for some reason this query needs to be isolated because maybe I want to set headers on this or something.

Speaker 1: And

Speaker 2: for the same reason why we haven't done this automatic batching yet, for the same reason, you cannot use set headers yet on queries.

Speaker 2: For the same reason, because we don't know, okay, do we disallow it only on like the dedicated batching query?

Speaker 2: Or do we disallow it by default?

Speaker 2: And then you're using something like query.isolated.

Speaker 2: And then you can do set headers on there because you know this is only ever going to be this one.

Speaker 2: And yeah, so it's still an open question.

Speaker 2: There's a few considerations here.

Speaker 2: So obviously less requests is better.

Speaker 2: At the same time, if we batch requests, then we have to do post requests because like we, or rather we cannot cache things between.

Speaker 3: Right,

Speaker 2: right.

Speaker 2: so yeah

Speaker 1: it's still an open question

Speaker 2: but we will solve it one way

Speaker 2: or the other at some point

Speaker 1: okay

Speaker 1: cool

Speaker 1: so then there's also like two more

Speaker 1: I think you briefly mentioned

Speaker 1: command which is kind of like the

Speaker 1: I don't want to use

Speaker 1: the web platform

Speaker 3: I want to

Speaker 1: use

Speaker 2: just fetch

Speaker 2: right I want to

Speaker 2: I require my people

Speaker 2: my users to have

Speaker 2: JavaScript enabled, right. And then you can use commands. And yeah, commands are basically

Speaker 2: where previously you were on your own and had to do regular fetches. Now you can use commands,

Speaker 2: which is integrated into the rest, similar to forms. So by default, commands will request

Speaker 2: nothing. So it's like inverse

Speaker 3: compared

Speaker 2: to forms because commands don't exist in the

Speaker 2: a non-progressive enhanced case and commands by its nature are probably much more granular.

Speaker 2: And so by default, they refresh nothing.

Speaker 2: And so basically you opt into what do you want to have refreshed by employing

Speaker 1: the same

Speaker 3: mechanism

Speaker 2: with the refresh.

Speaker 3: Oh, and what I

Speaker 2: didn't mention yet, you can also initiate this kind of single flight mutation

Speaker 2: from the client.

Speaker 2: So you can tell the command or the form,

Speaker 2: hey, please also refresh the following.

Speaker 2: And

Speaker 3: then basically

Speaker 2: we're passing in the remote function key

Speaker 2: from the client to the backend,

Speaker 2: and it then knows which thing to call and how to

Speaker 3: return it.

Speaker 3: And when you do it from the client,

Speaker 2: you can even do optimistic updates.

Speaker 2: So you can on the client say,

Speaker 2: okay, while this request is pending,

Speaker 2: I want to, I don't know,

Speaker 2: maybe you click the toggle button

Speaker 2: and then you're saying like,

Speaker 2: I'm sure this will work in 99.9% of the time.

Speaker 2: So I can just already do an optimistic update

Speaker 2: and I don't know,

Speaker 2: show that it's in thumbs up state already.

Speaker 3: Yeah.

Speaker 2: And that you can use, yeah.

Speaker 2: The specific APIs don't matter really,

Speaker 2: but you have the capability to do an optimistic update there,

Speaker 2: which exists as long as the

Speaker 1: mutation

Speaker 2: is pending.

Speaker 1: Yeah, no, that makes sense.

Speaker 1: So it's basically, it's like form, basically, command.

Speaker 1: It's form, but without using forms, in a sense.

Speaker 1: It's probably simplified a lot.

Speaker 1: But then there's also the last one, the pre-render one,

Speaker 3: which this

Speaker 1: one is pretty cool.

Speaker 1: I feel like you can pre-render parts of your site.

Speaker 1: Well, parts of your page, I guess,

Speaker 1: which you couldn't

Speaker 2: really...

Speaker 2: Parts of your data, yes.

Speaker 2: Yeah.

Speaker 2: So, yeah, that's definitely something

Speaker 2: which is really, really cool

Speaker 2: that before you basically had to do all or nothing,

Speaker 2: you could pre-render your whole page.

Speaker 3: Yeah.

Speaker 2: But what if your page is like 50-50 dynamic

Speaker 2: 50 and the other 50 percent is static um what do you do then you you could do it today by having

Speaker 2: like pre-rendered plus server ts endpoints and

Speaker 3: using this

Speaker 2: in your load function so

Speaker 2: there is a way today but it's not obvious it's it's clumsy so yeah with pre-render you're basically

Speaker 2: saying invoke this function at build time and then it's just like a blob that's lying around

Speaker 2: on your file system deployed

somewhere and then whenever a request is done you just serve that

Speaker 2: file

Speaker 1: yeah so an example and i think that the same example is that is done in the documentation is

Speaker 1: like if you have a blog site you could pre-render the the actual posts but then you could have

Speaker 1: the comments being fetched dynamically, for example.

Speaker 1: Correct.

Speaker 1: That'd be one example, I guess.

Speaker 2: Yeah.

Speaker 2: And the insight here generally is that

Speaker 2: the thing that makes your site slow

Speaker 2: is probably not going to be Svelte's server-side rendering.

Speaker 2: Right.

Speaker 2: It's going to be the data that's taking so long to load.

Speaker 2: And if you can pre-render as much as possible

Speaker 2: in as granular as possible beforehand,

Speaker 2: then this will speed up your sites.

Speaker 1: yeah

Speaker 1: very very very nice addition

Speaker 1: alright I think have I missed

Speaker 1: any of the remote functions are there

Speaker 1: secret remote functions there might be coming

Speaker 1: secret remote functions

Speaker 1: like if you do

Speaker 1: a cheat code do we get another

Speaker 1: one

Speaker 2: no hidden

Speaker 2: no hidden remote functions

Speaker 2: no

Speaker 3: April Fool's

Speaker 2: remote functions

Speaker 2: planned

Speaker 3: either

Speaker 2: yeah I mean I talked about batch caching so this is we are not sure if this will be like a another

Speaker 2: variant like query.cache or something where it's going to be part of the regular query we'll see so

Speaker 2: this may be another one but yeah no no immediate plans there for for

Speaker 3: more

so how how would we how

Speaker 1: How would we use remote functions today?

Speaker 1: We just enable it in the Svelte config, right,

Speaker 1: with experimental something?

Speaker 1: Right, so first

Speaker 2: you would--

Speaker 2: for remote functions, you would go in your Svelte.config.js

Speaker 2: and then inside the kit namespace,

Speaker 2: you would do experimental and then objects

Speaker 2: and then remote functions:true.

Speaker 2: And to actually make proper use of them,

Speaker 2: you would want to use that together with async svelte.

Speaker 2: So that means you also have to,

Speaker 2: at the top level, do another experimental colon

Speaker 2: and then object async

Speaker 1: colon true.

Speaker 1: We'll put that into notes as well.

Speaker 1: It wouldn't really make sense to use remote functions

Speaker 1: without the async function.

Speaker 1: Can you actually do that?

Speaker 2: I mean, you could.

Speaker 2: You could call them inside your load function.

Speaker 2: you could call them inside a

Speaker 3: weight block.

Speaker 2: You could also...

Speaker 2: So the nice thing about queries is

Speaker 2: they don't just return a promise.

Speaker 2: They also return an object with current and loading and error on them.

Speaker 2: So if you want to, you can use the flattened version of this,

Speaker 2: the non-blocking version, so to speak.

Speaker 2: And you could use that.

Speaker 2: So yes, theoretically, it's possible to use

Speaker 2: remote functions without async Svelte.

Speaker 2: But we will probably at some point

Speaker 2: basically require you to opt into that flag anyway

Speaker 2: because of how we will connect this

Speaker 2: with Svelte's resource

Speaker 3: API

Speaker 2: and so on.

Speaker 2: That makes sense.

Speaker 2: Yeah, and I mean, again, realistically,

Speaker 2: you're going to use this with await.

Speaker 1: Yeah.

Speaker 1: I mean, if you're going experimental,

Speaker 1: you might

Speaker 2: as well.

Speaker 2: Right,

Speaker 1: if you're going experimental,

Speaker 1: why not go all in?

Speaker 1: exactly

Speaker 1: all right

Speaker 1: I think that's

Speaker 1: unless you have something

Speaker 1: something else that you

Speaker 1: that you want to highlight

Speaker 1: I think that was a pretty good

Speaker 1: overview

Speaker 1: we should definitely mention that

Speaker 1: you've made

Speaker 1: I think you did mention it a bit

Speaker 1: the remote functions videos

Speaker 1: that you've done

Speaker 3: for the South

Speaker 1: Society website

Speaker 1: are really good

Speaker 1: So if you want to just have like a nice overview

Speaker 1: and you also do one where you talk about auth,

Speaker 1: like how to protect queries,

Speaker 1: because this might be something that we should talk about

Speaker 1: where in previously, like the load function,

Speaker 1: we kind of use the hooks to protect routes

Speaker 1: and protect stuff when it comes to auth.

Speaker 1: I mean,

Speaker 3: you could do it

Speaker 1: in the load function as well, I guess,

Speaker 1: But my experience was that you would just add an auth hook

Speaker 1: and then you would do protection in there.

Speaker 1: But now since the functions themselves are,

Speaker 1: you mentioned they were public, right?

Speaker 1: Anyone can call them.

Speaker 1: So you have to protect them somehow.

Speaker 1: Yes.

Speaker 1: So you could

Speaker 2: through it.

Speaker 2: I mean, the handle hook still runs before remote functions.

Speaker 3: Right.

Speaker 3: That will not

Speaker 2: go away.

Speaker 2: So you could still do it in the handle hook.

Speaker 2: But the difference is that remote functions are not tied to a page or a route.

Speaker 2: So that's the big difference to layout and page loaders

Speaker 2: because they are always tied to routes.

Speaker 2: And so you could in your handle hook be like,

Speaker 2: okay, everything under the path slash authenticated or something

Speaker 2: should be guarded.

Speaker 3: And that

Speaker 2: you can't really do with remote functions.

Speaker 2: And the way I would solve it is to have a, basically, I would say it's like a private query function, a shared query function, basically, which does all the authentication checks for you.

Speaker 2: And then you just call that inside your query function before proceeding to do

Speaker 3: the

Speaker 2: rest.

Speaker 2: And that's, again, that's the niceness of having this be just functions

Speaker 2: because you can just use regular function composition to get this.

Speaker 2: You could even create higher order functions from queries to, I don't know,

Speaker 2: call not query, but authenticated query or something, which does this for you.

Speaker 2: There's a few options you have.

Speaker 2: Is

Speaker 1: this the beginning of Svelte turning into React?

Speaker 1: No.

Speaker 1: Are you kidding?

Speaker 1: I mean

Speaker 3: the thing

Speaker 1: in React is all about functions

Speaker 2: right so and function composition

Speaker 2: I

Speaker 3: mean function composition

Speaker 2: is nice

Speaker 2: React hooks not so much

Speaker 2: throwing some shade there

Speaker 1: alright

Speaker 1: yeah thank you for coming on Simon

Speaker 1: thank you for having me

Speaker 1: do you have any picks?

Speaker 2: I don't think I prepared you for this

Speaker 2: sorry

Speaker 2: My pick is probably my new microphone. So after the first YouTube video I did,

Speaker 2: like before then, like every now and then I thought, oh, maybe I need a proper microphone. And I just

Speaker 2: not just like the built in crappy one in my headset. And then there was one YouTube comment,

Speaker 2: like, please get a better audio for next time or something. And I was like, yeah, you're right.

Speaker 2: I should finally do this.

Speaker 2: If I do more videos, then I probably should gear up a bit.

Speaker 2: And so, yeah, I got a...

Speaker 2: What is it called?

Speaker 2: Elgato Wave 3, I think it is.

Speaker 1: Sounds very nice.

Speaker 2: Yeah, I really like it so far.

Speaker 2: So, yeah.

Speaker 1: And I'm sure all your colleagues as well are super hyped about the audio being even better in meetings and stuff.

Speaker 1: yeah all right they can finally understand me yeah yeah okay so my pick is what i haven't prepared

Speaker 1: myself for this so i i think my pick is my new thunderbolt dock it has all sorts of fun

Speaker 1: functionalities i think it's like 20 ports or something just one cable connected to my computer

Speaker 1: and then I have two monitors and all sorts of stuff, Ethernet.

Speaker 1: It's a very nice experience just having one cable to connect your laptop.

Speaker 1: Yeah, the

Speaker 2: docking stations are really nice.

Speaker 2: Yeah, my Surface docking station is...

Speaker 2: Yeah, I would need a few more USB ports

Speaker 1: to be

Speaker 2: fully happy.

Speaker 1: This one I got had extra USB-C ports, which was very nice.

Speaker 1: So I think it has like eight USB-Cs.

Speaker 1: And then, yeah, it's a lot.

Speaker 1: And then two monitors and crazy, crazy dock.

Speaker 3: But yeah, I think that's it.

Speaker 3: Thanks, everyone, for listening.

Speaker 3: Again, thank you, Simon, for joining me.

Speaker 3: And we will talk to you next week.

Speaker 3: Bye-bye.

Speaker 3: Bye.

Creators and Guests

Kevin A. K.
Host
Kevin A. K.
Co-founder of Svelte Society 🌎 Organizer of Svelte Summit πŸ” Host of Svelte Radio πŸ“»
Simon H
Guest
Simon H
Working on Svelte at Vercel / Skateboarding for fun.
Remote Functions with Simon Holthausen
Broadcast by