About this video
What You'll Learn
- Initialize a Spin TypeScript app with spin new and HTTP routing.
- Use spin build and spin up to compile WebAssembly and run locally.
- Wire Spin KV and Ollama variables to generate personalized AI output.
Thorsten Hans walks through Spin from scratch: a TypeScript hello-world, an HTTP API with itty-router, persistence via the key-value store and SQLite, then plugging in Ollama with DeepSeek r1 through Spin's LLM interface.
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
0:11 Clouds amaze a tangled thread. Servers spin Hello, and welcome back to the Rawkode Academy. I am your host, David Flanagan, also known as Rawkode, and this is Rawkode Live. Rawkode Live is a show where we take a look at emerging cloud native technologies, patterns, architectures, and everything in between to show you how to be successful. And today, we're taking a look at a project that is near and dear to my heart, and that is Fermion Spin. If you have met me at a conference before, you know that I don't think enough people are paying attention to WebAssembly and server
2:36 side Web Assembly. And today, I'm joined by Thorsten Hans from the Fermion team to show us what we're missing out on. Hello, Thorsten. How are you, man? Hey. Thanks for having me, David. So first of all, it's great to be here and talk about Spin and WebAssembly. Yeah. I mean, thank you so much for taking time out of your day. I was literally just watching you on an hour long stream on your own channel, and here you are signing up for another hour long stream showing people how to do really cool things with WebAssembly and spend. So I really appreciate
3:10 you going back to back today because that that is no easy task at all. Yeah. Yeah. You're right. So I just jumped off the other stream. Right? So I had a quick bio break for two minutes or so and then jumped on on Rawkode Academy. That's yeah. It is what it is. I mean, it's intense, but we do, you know, we do it because we love what we do. Right. It's passion. And so, you know, passion led us here. So let's use that momentum and jump right into spin. Awesome. All right. Well, hello to Ravi. Thank you
3:42 for saying hello in the chat. Thurston, before we, you know, start showing people some cool code and cool technology, can you just take a moment to tell people a little bit more about you and what you've been up there? Sure. So, yeah, as you already mentioned, my name is Thorsten. I'm based in Germany, and I work as a senior cloud advocate with Fermion. So my daily job, you know, consists of creating samples, writing blog posts, engaging with the community, and also meeting or sitting down with customers or developer teams and help them to adopt WebAssembly
4:24 and spin as a technology stack to build serverless or reactive application, but applications, but also, you know, like build the or integrate spin and maybe SpinCube as a open source platform or runtime into their existing workloads or environments. Right? Because, you know, there are so many distributed applications out there and it's pretty rare that people can just, you know, wipe everything and start from scratch. Although that would be great and from time to time we as developers have that opportunity, but it's it's seldom. So, yeah, job is really to examine what's there and how could Spin and
5:21 WebAssembly help teams to move forward fast and to optimize maybe their cloud poster to lower their cloud spendings to run more efficiently. Bringing all those value additions from WebAssembly into a real world context. That's basically what I I do on a on a daily basis. Yeah. Awesome. Thank you so much for sharing all that. Now you mentioned a couple of things there that I think would be good to kinda just explain. So you mentioned SpinCube. We've already mentioned Spin. These were both donated to the CNCF within the last month. Right? So these are now official CNCF sandbox
6:07 projects. But I'm assuming some people may not know what SpendKube is. So maybe we could stay you know, just take a minute or two to tell people and fill them in on that. Yeah. That's that's a great maybe opening that discussion, right? Yes, so first and foremost, Spin recently got accepted to be a CNCF sandbox project And we are right now in that onboarding phase, you know, where things have to have to be moved around and, you know, we expect us to, you know, be on that small tiny CNCF landscape soon, really soon. So there there will be a spin logo
6:52 there, which is great. And so spin is in essence is a developer tool allowing developers to build applications using WebAssembly. So from a developer's point of view, you may think of WebAssembly as a compilation target, right? So we can write code, compile it down to WebAssembly, and then we leverage a so called WebAssembly runtime to execute our workloads. And in contrast to like, yeah, earlier like distribution formats, you know, WebAssembly has some nice features that we could participate when building WebAssembly, which is their distributable format is super tiny because we're just, you know, sending, let's say, the WebAssembly
7:45 module to that whatever runtime you decide to use in order to run that app. Right? So in you could think of a WebAssembly distributable or size wise, it relates to a container as a container relates to a virtual machine. Right? So it's maybe a hundred times smaller than a container. That's one of the, big benefits because pull times, especially in Kubernetes, are like ridiculously fast. Right? And we have, you know, strict isolation, strict sandboxing. So so the workloads are secure so you can run multi tenant run times and don't have to fear one workload taking down another one.
8:33 But yes, so Spin is the tool to, simplify the development process. And it doesn't matter if you're a TypeScript person, a JavaScript person, a Python, a Go, a Rust person person, right? Spin is like a unified CLI giving you a super simple CLI workflow. And we will look at that, right, by doing some hands on in a in a few minutes. So the Spin takes care of all those developer day to day tasks for you. And then we mentioned SpinCube like two times right now. So SpinCube is also an open source project. It's a sub project under the spin project.
9:18 It's also as part of our contribution to CNCF. It's also in CNCF as sent on the sandbox layer. And that's basically a way of running those spin apps in Kubernetes without leveraging containers because, you know, we wanna take or we wanna make use of those values like small binary size, incredibly fast instantiation and runtime performance. So SpinTube, again, to emphasize that is also open source, is also part of the spin project, which means it's part of the CNCF sandbox tier and could be installed to like, any arbitrary Kubernetes cluster. And it's a I think as a final
10:15 statement to spin queue before looking at it, I think it's a perfect example of how versatile WebAssembly run times could be. Right? There could be like a way, oh, SpinCube is the way of running it in Kubernetes, mimicking like ports and all those primitives that we know and love from Kubernetes. But maybe you're more onto, I wanna just build a workload, throw it over the wall and get it executed somewhere. That's for example, where a different runtime, which is firming cloud hosted service that we provide enters the stage. That's just a different way of how those WebAssembly modules are executed,
11:02 are managed under on that platform there. Awesome. Yeah. Great explanations. Thank you so much for sharing that. We understand spin. We understand spin queue. I'll add one little bonus nugget there, and it is that, you know, I get to sit and play with a lot of technology. Like, every day, I just I'm like a technology magpie. Right? And if it's shiny, I play with it. And I've gotta say, for anyone watching this video, pay attention to the developer experience as source them as part of the first stuff. Because I really believe that spend have just absolutely nailed it. The the
11:37 way that you build components and add components and iterate of building a real application. I I I just think it's unmatched. I hope people enjoy that and appreciate that as you're hacking on your demo. Now, with that being said, what are we building today? Well, so, yeah, obviously, David and I were chatting in advance of that stream a little bit about how could we, you know, provide most value for, like, newcomers that have never experienced WebAssembly, never experienced Spin, and for those who may have already used Spin. So the idea is to start with a
12:15 Hello World to give you like, you know, to make the audience avail or aware of like those the Spin workflow. And from there, we start like implementing, for example, an HTTP API. And last but not least, there could be no session without some AI. Right? So we will bring in some AI capabilities as well. Just to give you a rough idea of how would I maybe integrate an upstream service that I have already in my environment that I wanna use from within Spin. So that's the idea. Awesome. Alright. I got a comment from Indigo VFX
13:03 saying that my volume is very low compared to yours. So, hopefully, I fixed that. If not, I will keep on tweaking. So please let me know, Indigo. Thorsten. I I turned myself up, Thorsten. I I think it's me that's the problem. It's always me that's the problem. Do you want to share your screen? We'll move over there, and we'll start having some fun that was spent. Yes. So I now you should see my screen. We do indeed. I see you. I see the Fermion developer homepage. I am excited and happy to let you take stage.
13:41 Awesome. So let's get rid of the notification from Chrome over here and let's close that tab. And yeah, as you already mentioned, we are looking at the Fermion Developer Home because I think that's a good like resource to get started depending on whatever the preferred language is that you wanna use to build WebAssembly workloads or spin apps, how we call them. So we have documentation in place for Spin guiding you through, okay, what is Spin? How to install it on different operating systems? And then we have language guides. Right? Because there is like, you know, there are so many languages that
14:26 you can use to build WebAssembly workloads. We have detailed information on, for example, how I wanna use rust, right, for example. So what do I have to install? So you need rust, you need a given target, the wasn't 32 was a p one target. So there's documentation for like our main languages, but also for other languages that are that you could take as of today and compile down to WebAssembly. But I think we should not spend time on, you know, telling people how to install Node. Js in our example, because I think that's a problem already solved out
15:11 solved and we should get started by taking Spin for a spin. So I'm on macOS and I'm a Homebrew user. So I installed Spin using Homebrew. However, there's also install script that you can just curl and execute on your machine that works across Linux, different Linux distributions that works on Mac OS as well. And obviously for Windows, we provide binaries that you can download and use on a on a Windows machine. I haven't used Windows, to be honest, in the last ten years or so. Looked into quite good these days. I mean Yeah, it is. You know, I always struggle
16:02 with what I refer to as the device romance. That's like having like a brick like my MacBook Pro with Windows. Okay. Maybe that could be an option that I could could look into. But yeah, I'm always lacking like those those those nice capabilities provided by Apple Silicon and their battery life. Anyways, we're not here. So I run NextOS. So for everyone else who's in that Next ecosystem, there is a Next package for spin, and it works great. Unlike you Awesome. On your Apple device, I get about three hours battery on my laptop because it's Linux and x 86. So what
16:41 can you do? Yeah. That's I think yeah. We could go deep on recommending different recommending different hardware and operating systems. But I think the most important takeaway over here is that Spin is available for all those popular operating systems, for all the architectures on those operating systems that people ask for. Right? So for Windows, there's an x 64 build for macOS. Obviously, have Intel ARM builds for Linux. We have Intel ARM builds. Yeah. So choose your poison and then install Spin using your preferred way. I know a few people that use Brew on Linux, although that felt alien to me,
17:34 but that works as well. Awesome. Okay. So I obviously have installed Spin. Let me bump the font size. I think this should work. Yeah. I mean, that looks okay for me. If anyone has any issues reading that, please let us know in the comments and we will increase it some more. But I think it looks good, man. All good. Okay. Cool. So I have installed Spin on my machine. Let me quickly do a Witch, Spin over here. We also have a version manager for spin, but I wouldn't go that deep into detail of the different plugins that I'm running right
18:09 now. I think most important takeaway is I have a spin executable in my path and I can use spin version to determine, hey, I'm on a on a recent version of Spin. And I have installed NVM as my node manager. And right now I switched to node 22. So I think that's the latest LTS or the most recent LTS version. So the idea is to start from scratch, build a hello world in the first place. So the spin workflow is I have my language specific tooling installed. We're right now wearing the hat of a JavaScriptTypeScript
18:51 developer, right? So I stumbled upon that spin new command in the spin docs, curious about that. I execute it and I get asked by the CLI, hey, just pick a template to start your application. Right? So there we provide different templates. You can roll your own. The community already created several templates that you can just install from a git endpoint or so. And let's say we wanna roll with TypeScript for the sake of this example. So I choose the template. I provide a name for my app. And as I promised, we do a hello world
19:33 over here. We skip the description for now. And as I as I've chosen HTTP TS, so before the hyphen, there's the the kind of trigger. So what will actually make my workload run? And it is obviously incoming HTTP requests in that in the case of HTTP dash. So I have to tell Spin on which routes it should invoke my WebAssembly application. So forward slash and three dot basically means everything starting from the root route and all its children routes below that. But we could be more precise. We could, for example, say I'm building an API
20:22 and I just want to respond to incoming calls on my listener that is like a port or a custom domain name, followed by a forward slash, followed by the API prefix and then everything underneath that. But let's stick with the defaults for now. Okay. So what we got right now is we got a new hello world folder. If we move into that one and explore that for a second, we see a lot of familiar file names from a typical TypeScript project. Right? We see a package JSON like in every JavaScript project. We see a TSConfig JSON, a Webpack config. So
21:08 it should look pretty familiar for a JavaScript slash TypeScript developer because we wanna align or our intention is to align with the idioms that are like, you know, set in stone for whatever language you choose. Right? Are there any plans to support you know, obviously, this is using node 22. We've got the TS config. We see Webpack's being used for the, you know, the bundling aspect of it. Are there any plans to support something like Deno or Bard an alternative TypeScript runtimes in the Or is it kind of tied to Webpack for the time being?
21:45 So regarding bundling or packaging like those modules, we I think there is no plan on replacing Webpack right now. Regarding the underlying runtime, so with I think couple of months ago, we released version three dot o of the Spin SDK for JavaScript. That relies on a runtime called StarlingMonkey. So StarlingMonkey is like is a JavaScript runtime maintained by the bytecode lines that we package together with your application source code into the web web assembly binary that you distribute across like boundaries across, you know, maybe you upload it to Firmware Cloud. So your app is fully functioning
22:43 and you don't need any kind of JavaScript runtime on this server or this serverless platform, right on the on the target, so to say. So regarding runtimes, we recently switched to StarlingMonkey regarding bundling and tooling in the JavaScript templates. That's what we currently have. I would love to, you know, chat about what's the current state. Is Webpack still the cool kit? Or is everything is everyone moving away from that? I will be super happy to to get feedback on that one and to bring in like a simple variable. Right? Those templates can ask questions. So by default, we right now use NPM
23:30 as a package manager. I've already created a PR to bring in YARN and p and the other one p n p n, I guess, if I remember correct. Yeah. P n p m is still We still have to file a PR for that one. But yeah, we are continuously improving those templates to meet the community where it is. All right. Thank you. So there's one addition. Oh, sorry, did I interrupt you? No, no, no, you didn't. I was just saying thank you. Okay. Now I thought you were just taking a break to ask another question. But anyways.
24:17 So this is, as I said, should look familiar for JavaScript or Node developers with one exception that being spin dot toml. So if we open up the folder in in code, for example, to browser to, you know, move around in those files. So the spin toml, that's the application manifest where all the things are pulled together. So we have some fundamental information about the application. We have, okay, configuration for the trigger. So what is basically responsible for making my my app run. Right? That's incoming HTTP requests on all and on all routes. If we receive such an HTTP request, we
25:06 wanna invoke the hello world component. And for components, we always have two configuration tables. One is the component configuration where we specify, okay, the wasm module is here and please exclude the following files. And we have build instructions. Because if you think about, you know, building more complex systems with spin and maybe use Python for one where Python makes most sense. You use Rust for something where you wanna get wanna have best performance and you wanna use TypeScript for a different component where you wanna meet the developer or where your I don't know, your teammates
25:55 are most confident in writing TypeScript. So you may end up with a mixture of languages. However, we think that the DX, the developer experience should be consistent across those languages. So spin gives you a spin build command. Under the covers, we execute whatever's mentioned over here for this particular component. We also have like a spin watch command so that you can while you code behind the scenes, the app gets recompiled and you have like those hot reload capabilities. So whenever you execute a spin watch, whatever is mentioned in line 19 will be executed for the
26:40 sake of this component. So basically, it's that, you know, translating an agnostic workflow into a language specific workflow. That's basically what we have over here. I think the component configuration is something we will revisit for a more realistic demo in a few minutes. But for now, think of it as fundamental or basic component configuration. So that's really the only special thing in a spin application is the spin TOML file. We have a source folder with an index TS and we decided to provide or to use or to add IDRouter, which is like a popular router in a
27:29 in a known ecosystem that's not tied or tightly coupled to a framework. We just wanna have a router over here. However, you could also use Hono, I think is the other popular router in an old world to create routing capabilities. So this is basically the hello world example. Right? So we register to get routes or API will only respond to incoming get requests at root route or at hello. And as, you know, you may already guess, there is a route parameter called name. So it does not matter if I if I send a get request to hello David
28:10 or to hello slash torsten. We expect that to be returned via HTTP. So like a personalized greeting API or personalized hello world. So let's do some housekeeping over here. We have some squiggly lines because I haven't, but because I did not run NPM install so far. And we think that something that should happen automatically. So let me move back to the terminal pretty quickly. I'm in the hello world folder again. So this is where SpinToma resists and I'd simply do a spin build. So right now it's double checking, oh, Torsten decided to use TypeScript and the build instruction
28:57 tells the tooling, hey, run an NPM install. That takes a while right now. For that. It's pulling down the dependencies. And right now it's executing the build instruction. So there is a build command and let me go back into package JSON. So there is, you know, we use some tooling that we created and again, upstream tooling from the bytecode alliance to take your JavaScript source code that could just, you know, be one file that could be like a regular project layout. It doesn't matter. And turn that into a WebAssembly module. So we end up by executing that spin
29:44 build with a hello world dot wasm file. If we now look at lsla dist was the subfolder, we can see, oh, let's add an h over there. So that's 12 megabytes in size for Hello World. But it's not just our application code. It's also the JavaScript runtime that I mentioned earlier. Starling Monkey is part of this distributable. Right? So how does that work? Behind the scenes, we take and I think that's super that's nothing, you know, people have to be aware of when building their first applications. But to me as a developer, I think this is super
30:32 that's just awesome technology what happens on the covers. Right? We have this as the user code or we often refer to as the guest code. So that will become the WebAssembly module. So we take your, that code. We take that StarlingMonkey JavaScript engine. So we start that engine during the build. We load the user code that loads, you know, a vast amount of node JS dependencies the entire tree depending on what we import and evaluate the global scope. And once we have evaluated the global scope, we take a memory snapshot from that and write that to disk.
31:21 Meaning for every invocation of our app, there is no bootstrapping of the JavaScript runtime because it was bootstrapped. The dependencies are evaluated. The global scope of my custom code, of my guest code is evaluated, and the app is ready to handle the request, resulting in JavaScript code compiled down to WebAssembly having a better cold start performance than JavaScript code. And that's just, you know, when I stumbled, when I, you know, discovered all those internals and what happens during build time for interpreter or interpreter languages like JavaScript, Python, that's just, yeah, kind of mind blowing. It's super awesome to
32:09 see how far we could push technology to optimize for perf for performance. That's pretty cool. Okay. So we build we executed spin build, tossed and got sidetracked, talked about all the internals. Sorry for that. But now let's close the loop. So we have spin up, which basically takes your WebAssembly module and starts a listener on your local machine to, you know, serve as an inner loop as a, you know, development time web server and every invocation. So for every time I will move to the second to a new terminal instance over here and I will do a curl
32:55 local local local host 3,000. Right? And we get a hello universe and right now let's say hello David over there, we get a hello David. So for every request I sent to localhost 3,000, a cold start happens under the cover. So every time the WebAssembly module is loaded, first time it's loaded from disk, second time it could be loaded from within, you know, the the heap because we know it was already there. Chances are high that it will be requested again. And then a clean instance is executed. So there's no shared state. Right? Every invocation
33:41 is a cold start. And the the amazing, like, performance we can get out of that is, for example, I think number of requests is this and this is concurrent threads. And let's call yeah. Let's just invoke hello universe over there with 100 concurrent threads 10 k. So we are able to I think we are we logging? No, we are not logging. So we are able to where's the histogram? A distribution there is 99% of the request in less than o dot o five six seconds and you know without, putting pressure on my machine at all because
34:27 those modules are so tiny in size and could be instantiated and invoked so fast. So this is a pretty bare bones hello world. Right? Like find new TypeScript, http TypeScript in this case, doing a spin build, doing a spin up, and then I can run it on my local machine. Or if you wanna deploy it to, for example, Fermion Cloud, we have Spin Cloud, help. So everyone could register for free and run five apps for free over there. Or you may wanna leverage SpinCube. Let me quickly bring up the website for SpinCube. This is again open source. You can deploy
35:23 that to your Kubernetes cluster. So we have, you know, documentation on how to install it with Helm, how to roll it on Rancher Desktop on different like Kubernetes distributions, so to say. And I think we shouldn't go down that path of setting up SpinCube right now. From a developer's perspective, at some point we have to hand over with operations people or DevOps teams. Right? And we think as a developer, there should be like, oh, we are responsible to a certain degree of for getting the app into an environment. And in our cases, obviously we have to
36:11 somehow share the app. So instead of reinventing the wheel, we decided, hey, you we integrate with TTL, SH for example. We integrate with what's out there which OCI. So you can simply ask Spin to create an OCI artifact and distribute that through registries like Docker Hub or like Azure Container Registry or TTLSH which is like an anonymous and ephemeral registry that I use for demonstration purposes. Right? So this right now takes the WASM file and the SpinTML, package that together and uploads that as an old OCI artifact, meaning that the Kubernetes cluster with SpinCube can pull that and run it.
37:05 So the overall end end to end, the OCI artifact will be 12 megabytes in size plus those 404 bytes for the SpinTML file. And yes, to be honest, JavaScript due to the runtime and Python are the biggest distributable. So Rust, Go, those are obviously tinier in size because we don't have to ship an entire runtime as part of the application. So for Rust, with all optimizations, we could go down to two digit kilobytes in size for the distributable. Okay. So speaking about developers responsibilities. So we shared our app right now and it is available under this tag, right? So
37:56 if people are following along right now, they can do a spin up dash dash from pass in that, what is it, tag? No, no, repository or the ref the OCI reference. And then your spin CLI goes, pulls that OCI artifact and spawns it from that OCI artifact. So how do we get into a platform like Kubernetes? So we think we should not reinvent that wheel, however, provide a great developer experience. So we that we say spin cube scaffold from t t l s h and without me adding more flags to that command, I get a Kubernetes deployment manifest. So that may look
38:49 familiar for those viewers of, or for you David as I know you have like a like a whole lot of Kubernetes experience. So you could already guess some things from this this YAML. There is a CRD that will be deployed as part of SpinCube to your Kubernetes cluster. We have a concept of a SpinApp executor. So we are using a container, the shim implementation under the covers to run those WebAssembly workloads instead of running containers. And we want to horizontally scale this to have two replicas. And we think our story with deploying Spin apps into Kubernetes
39:37 should end here and integrate seamlessly what you're used to use. Maybe you wanna or let me simply switch to a Kubernetes cluster. So maybe you're cool with kubectl into into your cluster. Right? If you're cool with that, you can oh, no. Apply. I was missing apply. Oh, come on. If you're cool with that, you can do it. Right? If you wanna use GitOps, cool. You can GitOps this manifest obviously. If you wanna build a Helm chart around it, go for it. Right? We think we should meet the developers where they are or in that case, the integration,
40:21 the DevOps teams where they are and don't try to come up with fancy ways of bringing stuff into Kubernetes. Can you run a command for me, please? Sure. Kubectl explain spinapp.spec. Oh, yeah. You've already run before. Yep. Yep. Yep. That's yep. That's That's how much we can interact and and and play with scheduling, you know, the spin workload on Kubernetes. But that that looks pretty comprehensive. That's that's pretty cool. Yeah. So we have so with SpinCube, what so leverage kWASM, which is another open source project under the SpinCube organization that's basically deploying that containerd shim for spin
41:12 to the Kubernetes nodes. And then we use a SpinAppExecutor. Right? I we saw that over here, Executor. And a SpinAppExecutor is basically another CRD oh, let's let's go to get SpinAppExecutor. So I have my default, and the SpinCube documentation guides you through that. Right? And it's basically using the runtime class concepts of Kubernetes telling telling the scheduler, hey, instead of scheduling a container workload, schedule a workload that's using the WasmTime Spin v two runtime under the covers. So it's loosely coupled and allows you to, yeah, to run SpinApps in Kubernetes. So we can get a k get
42:06 SpinApp. So you see a couple of spin apps deployed there. One is scaled to 20. And for every spin app in SpinCube, you get a port. All ports are running. And the nice thing about let me k edit spin app. What is it? Hello spin. So this is scaled to 20 replicas right now. Let's go there and scale it into two. So this is manually scaling. Right? We integrate with KEDA, HPA, blah blah blah, you name it. Yeah. So so we should be down to way less ports. And the value of small binary and ridiculously
42:54 fast instantiation and invocation is gets visible when I do the when I inverse it again. So if I do a spin app, edit spin app again, go over here and say, wanna have 20. I save it, I do k get p o and there we are. Within a fraction of a second they are all up and running because nothing happens. Right? The image gets pulled and the manifest gets obviously interpreted. Right? And things are checked and the listener is already there. Right? So we spawn a subprocess on the note, but the app is every invocation is a
43:38 cold start. Right? So within a matter of seconds, we are up and running with like 20 instances. Yeah. So we consider the the scale that we use for these pods as it's really it it's a web server. Like, we just it's like a proxy. I know it's not technically, but the requests are coming in, and then it's CGI CGI ing out to the spin application. So that might help make sense for how do we know when to run one of these polytorsies 20. And it's if we just pretend on our head, again, even though it's
44:10 not, that it's NGINX, that might help make it easier for people to understand. We also have a few questions in the Sure. The comment section if you're happy to answer those. So Indigo VFX is asking, is there a performance difference between the various workload languages? So if I write something in HTTP Rust versus HTTP JSON, even though the WASM binaries are different sizes, is there a runtime performance? Yeah. So definitely, definitely Rust is by far the fastest or resulting in the fastest binary or application. Right? Because although we, let's say, warmed up the JavaScript runtime, we
44:52 loaded the dependencies, we evaluated the global scope, there is still the like run time overhead that's introduced by using a interpreted language like JavaScript. So Rust, Golang, like non interpreted languages will always be faster. Also because distributable size is smaller and at some point especially with when using Kubernetes from cloud vendors, disk IO becomes also a problem because loading a 12 megabyte WASM file from disk is slower than loading a 30 kilobyte or hundred kilobyte WASM file from disk. So yes, there is a performance difference and we always treat it that way. If you are looking for best performance,
45:48 go the Rust way. If you're looking for a convenient developer experience and easy to adopt like SDKs and stuff like that than TypeScript. Maybe the smarter choice and Go is the nice thing sitting exactly in the middle giving you better performance but still having a a language that one could learn in like a couple of days at least to get going, which is slightly different for Rust, if you ask me. No shit. Alright. Thank you for answering that question. I hope that helps Indigo. Ravi also has a question and is curious if spend supports open telemetry.
46:31 So Test off. We'd instrument ourselves within the spend application, or is this something that the SpinKube runtime handles for us? Maybe you could shed some light there. Yeah. That's that's a great question, Ravi. Thanks for bringing that up. So Spin does automatic telemetry or Spin produces telemetry. And we integrate seamlessly with open telemetry stacks like for example, we have a plugin that you could spin auto setup for example with Aspire or with the default stack is I think Jager Prometheus or you know all the entire you know the entire tool chain that that's used to
47:24 to build the OTEL stack. I like Aspire because it's like that single container developer in a loop stack. So if I do a spin OTEL up, I think, and let me curl the app again 10,000 times and right now do a spin hotel open aspire. So we should see automatic telemetry automatically being populated. Our app does nothing right now, so that's why we only have two spans per invocation. But we add, you know, interesting boundaries. So for example, if you call a kv store, what we create obviously spends and correlate them up into that graph.
48:20 We provide fundamental metrics, but that needs a couple of minutes to be picked up by Aspire. So we will see like invocation count being being populated there in a few seconds. And obviously, we also get if we lock to console. Ly what we don't do in Hello World right now, we will see structured log output over there. For guest logging or guest telemetry, so basically you as a developer creating your own traces and spans. A colleague of mine, so Caleb, will do a talk next week at WASM.io, 1 of the biggest like WebAssembly conferences in
49:04 Europe. And I think that should be up on YouTube in shortly after the conference because there are some in WebAssembly world, is WASI, the WebAssembly system interface. So there's a lot of standardization work happening to ensure that you can build a spin app with spin and run it on a different runtime. So and the same thing is in Wazi, there is there there are efforts to say, hey, across the board, these are the APIs how guest code should do traces or should do metric populate metrics. And Caleb is somewhat leading in or in the group of leaders when it comes to
49:56 how OpenTelemetry should or could work in Wasm. And so I think that one is definitely worth checking out. For now, you can, if you, for example, in JavaScript do a console a log that becomes a log that is, you know, sent to standard out or standard error if you log to that one and that those are sent to the corresponding log streams there. But we don't have support for traces or custom spans or metrics besides using maybe Ravi if you have experience with Prometheus, you can obviously use the push gateway, right, to push metrics into your Prometheus.
50:40 That's definitely possible. But we are, you know, moving forward and trying to come up with a API that's that works across all the different languages, that's easy to understand and that allows developers to generate all sorts of telemetry data. Yep. I hadn't seen that Aspire workflow before, but that was that was pretty slick. So I was really happy to see that and and looking forward. Now to Caleb's talk on how we do guest and custom spans because that would be very, very cool also. So I hope that answers your question, Ravi. Yeah. That's I think that's the next logical
51:22 step. And for for SpinCube, you can obviously I think there's a flag somewhere that you can say, hotel collect hotel collector exactly. And you say, hey, there's my hotel collector. And then the SpinCube runtime will forward all those telemetry data to that. Cool. So next, we created a Hello World. I think we could take it one step further with the JavaScript API. Also, because I think, David, you mentioned that you are you think the SpinDX is great. And I think we could, you know, demonstrate a little more of the SpinDX to make people aware of
52:13 that great developer experience. So let's do, this is super unscripted. Let's do a spin you. And I think it's I always try to get a sense of the community. And by having a community that engages and ask questions and is curious about perf and stuff like that. That gives me like the confidence. Okay. I could bring in some more details and share maybe some not get best. I hate the term best practices, but let's say good practices. So I find it super valuable to have an HTTP empty template. And let's say this is crud. The HTTP empty template is basically just splitting
53:05 out a spin TOML with almost no content. So no trigger, no component, nothing. Because maybe you wanna build something that consists of more than just a single component. And then the HTTP empty template is what you what you're looking for. It's like if people have experience with Microsoft or .net or in Visual Studio, this is like a solution. And now we are adding projects or different workloads to our solution. So we can simply say spin at over here and let's say again, let's start with TypeScript and let's say this is my API and I wanted that to respond to incoming
53:49 calls underneath API. So if we look at spin. Toml right now, we see, hey, right now we have a trigger and we have build commands, component configuration, but all is pointing to work here. So the folder structure oops. The folder structure has right now an API subfolder. Right? So we can organize different workloads and subfolders to to, you know, work seamlessly with mono repo, organize or mono repos or, you know, to organize things in a more meaningful way. So we may add a front end for example. So how could we do that? We have the static file server,
54:38 which is basically a web assembly module we created that's able to serve files from a predefined folder and return them over HTTP. So this is like a let's call it a recipe, right? For I have an idea, I wanna build an app that app uses maybe Vue. Js, Angular, React, whatever to build a for building a SPA, a single page application. And I wanna have an API next to it so that the front end can immediately talk to the API. So static file servers what we are looking for, we call this thing front end. And we say we wanna serve that on
55:20 the root of our port and we wanna have all the files for the front end resist in the front end folder. And now we have a nice structure again, let's open Oh, let's maybe do this one and let's open code in front end and let's implement our single page application and at the beginning that should be as easy as this. So this is our front end, Right? It's more about orchestrating the things and running them instead of me implementing a single page application because that will look not that great. I'm not a UX guy. I'm not a CSS
56:02 guy. Yeah. So bear with me. But that's Why is the close button not working? I don't know. So that's that's Let's say that's our front end. Right? So we have two folders, but still the workflow is the same. We do a spin build. It iterates over all our components. It checks, do we have build instructions for that? If so, I execute that for the API. It's again doing an NPM installing all the dependencies. That's what takes a second right now. Then it's compiling the API down to WebAssembly. Then it checks for the front end. I
56:44 don't have to do any build steps. So I'm done. If I do a spin up right now, and I can see I have two available routes. So I can immediately browse low close 3,000 and get back my my document. Okay. Maybe it's worth adding a little more content to our HTML page. Right? But it is my HTML I created a second ago. And I have a listener right on the API which returns with a four zero four by default. So now we can maybe spend a couple of minutes in laying out a simple API that's more than just
57:30 that's more than just to get endpoints over here. Right? It gave me a four zero four although there is a root route registered. But when adding the component, I said it should listen to API forward slash. So I have to mimic that obviously over here. So this would make the app respond to my port my assigned port forward slash API and so on and so forth. So I said there are some more nuggets in SpinCLI. So for example, we are so far we only talked about the trigger, right? About things that, you know, make our app run. But somehow
58:18 we have to store data, retrieve data, and Spin has an SDK for TypeScript, for for Rust, for Go, for Python, that simplifies things like that. So for example, let's say we want to build an app to retrieve to retrieve values and want to retrieve values by a certain value by key and also we have a post API to set a value. Let's get rid of that. And that's what we basically handle set and we will implement that handle get by key and this is handle get all. Right. So that's what we're going to implement. So,
59:15 you know, you recognize there's a colon followed by key, that's a route parameter. So we can pull that by saying, I'm not interested in the request. So this gives me basically access to the first argument gives me access to the incoming request object. And the second one allows me to pull data, for example, from the route parameters, the key in that case. So let me come up with that. So right now TypeScript TypeScript compiler transpiler would yell at me that I'm not using request. So I can simply say underscore to ignore it and pass in the
59:54 key into my handler. And for this one, we will update that one in a second because we wanna use the request payload. Right? Because user should send a post request with JSON. The the payload contains the value and the key is part of the URL and then we store that in key value stores. Let me quickly create the stops for those functions. So let's say const, I like that style of defining JavaScript functions or TypeScript functions in that case. Okay. So this and this. So here we get a key which is a string. Over there, we get the key which is
1:00:44 a string. But in addition to the key, we wanna use the request payload. So let's let's call it the body and that's of type array buffer. And to get the array buffer, that's a stream. We can read the stream and we wanna do that async. So we can say await request array buffer. And now it's almost we are almost in good shape, but we don't return anything back. So let's make it happy over there by saying return status 500 for now. So this is basically reminding me to implement it. Okay. So this is the shape of the
1:01:31 API. So now the question is how? How can we leverage like a data storage? Right? And with the Spin SDK, give you like super simple yet valuable APIs to achieve day to day tasks. And one of them is, for example, using key value stores. So WebAssembly has a capability or strict sandbox, a capability based security model. And by default, your code is not allowed to use anything. It's not allowed to do outbound HTTP. It's not allowed to read environment variables. It's not allowed to do outbound TCP and talk to key value store. So you have
1:02:18 to explicitly grant the permission. And this is like, although it might sound super tedious in the first place, but it's giving you the confidence that your code is only doing what you wanted it to do. And also for the teams or for the people that run a platform that runs workloads like that, they have the confidence that no matter what the user code does, it aligns with all the capabilities that are specified in its manifest. So that's one of the if you ask me, that's one of the greatest values provided by WebAssembly. Okay. So how do we do that? How
1:03:05 do we give our app access to Kivelli store? So again, in Spin, we try to simplify that as much as possible. So we open up the folder where our spin toml resists and we have that component configuration over here. So allowed outbound host is the property that you can use to, for example, allow my API to send requests to httpsapi.github.com. Right? Without this being explicitly mentioned here, outgoing fetch calls or HTTP requests to that origin will fail. And the same applies to key value stores and SQL lite databases and so on and so forth. So there
1:03:53 are like they are all documented obviously. So I wanna give my app access to a named key value store called default. You can use different name, but then you have to provide configuration like connection string, where that resists. But default is like a super convenient way of doing that because if we do a spin where are we? We are there. If we do a spin build again, it should still build everything as we saw before. Okay. If we do a spin up right now, you can see storing default key value data to dot spin key value DB.
1:04:39 So the CLI knows, hey, Torsten wants to use a key value store, instead of he us asking him to run a key value store in a container or something like that, let's simply roll a SQLite database for him that acts as a key value store. Right? If that if that's cool for me, I can use that. If I wanna leverage Redis that's out there in my cluster, I just have to provide a configuration aside or besides my app and tell the Spin app to use a runtime so called configuration file. So I don't have to change my
1:05:19 code to point to Redis instead of the developer key value store. Alright. This is the SpinTML. This is our implementation. How could I right now retrieve all the keys from the Key Value Store? So this is where we can use the key the the Spin SDK. So there is a Spin SDK. Why is that not okay. Let's use KV and then we can either open a named key value store or we can say, I wanna leverage the default key value store. So right, on my machine default is right now using SQLite. I can override that and can say default
1:06:08 right now points to Redis. So it's just a name to loosely couple the application implementation from its configuration. And key value has or exposes like simple APIs. So for example, get keys. I can simply call that. That gives me all keys. And then I can say const values equals all keys. Let's say, now we can use a map, so there's a key and we take that key and then we return key being key and value being k v get, let's say get JSON, no get JSON, that's like this. That was rust. Sorry for that. And then we return
1:07:03 like whatever is there in values and instead of returning an HTTP 500, we simply use the JSON helper from the ET router to return the entire contents of our key value store. Right? So again, and I think this is super important. The idea is from the Spin SDK to provide simple yet useful APIs to speed up your developer, performance when building, applications for the cloud or for a serverless platform. Cool. Let's quickly implement the other two's because that's super easy. We ah, maybe interesting thing, there could be network in between our spin app and the
1:07:49 key value store. Right? So if something fails, if a error is thrown and it's not caught by your guest code, spin catches the error and returns an h t p 500. Right? So that's obviously that's that is and should be used. You should use try catch to catch errors and you know, handle them with care and handle them correctly. But in the worst case, the surrounding or the underlying runtime catch the error and returns an http 500. Alright. If the key is faulty, meaning if it's empty, undefined, null, we return a new response and saying this is
1:08:38 a bad request and setting the status to 400. Okay. Otherwise, we say, hey, if not KV exists at position key, we return, let's copy that. Nope, let's copy that and say, this is a four zero four not found. And if it exists, we return JSON kv get JSON at key and are done with implementing the handle get by key. Last but not least, they're set. So we expect our code to provide a payload once JSON parse. So this is a new int eight array and we have to turn that into a string. We can use a decoder,
1:09:36 new text decoder by default UTF eight which is the right one. So we can say decode body. So we end up with something turned into an object by using JSON parse. This could fail, right? Error. So we could again return bad request. Or let's do it like this. No, no, let's do it, let's do it like this. Okay, so if we get a payload, let's say if, let's accept any payload for now. And let's say, const kv equals kv open default and let's say kv set at key, our set JSON JSON. At position key, we wanna set the entire
1:10:39 payload. So whatever the user sends over the wire, this is not a good API, right? Because we accept everything from the user. So we have at least we should at least do some housekeeping over there. Maybe if key if not key return new response that that request status 400. Maybe let's move that over there. And let's say or not payload or not payload dot value. So at least some housekeeping, right? We should not trust what arbitrary clients send to our rest APIs. And I hope that's somewhat, you know, aligning with that. So, but if everything works great, we wanna
1:11:36 return a new response and we simply say, no, because we didn't wanna set something there and we set two zero one or maybe we set another header, headers. That's the location header over there and we set that to API, what was it? Values, Values at position key. And I'm messing up this one because status. That's correct. That's correct. This is that one. This is that one. Okay. What's going on there? Return new response. I don't provide a body and then I why is it green? It looks weird. Expression is not capable. Status, I had there's
1:12:39 status two zero one. Did I did I make a typo? No. Headers is an object. Object gets if I do that. Is Permanent location doesn't need wrapped in quotes. Yeah. No. I think it's not it doesn't want backticks there. That's weird. Doesn't need anything, right? Like it's just the location. Yeah, you're right. You're right. It could be just like that. Yeah. Good call. Yeah, that's good. All right. So this would basically be a simple CRUD API backed by key value store. Let's try it. Let's go there and do a spin up dash dash build. So you also can, you know, save that
1:13:31 spin build command and run both in one. And we should end up with, again, an API running on 3,000. Okay. Let's take this one. Oh, no, let's go away. Yeah. Let's take this one and do a curl I x get values and we expect an empty array. Okay, so let's do ix post and post in content type application JSON and data being value value. Hello. And there is a double quote missing. Push post that to that end. Oh, 404. You need a key in the URL, so values slash. Thank you. Thank you. Thank you. Foo.
1:14:38 That was a good one. Oh. Oh. Destructed parameter is undefined. Oh. I think you used dot values in the code instead of value. No. I think this is this is my mistake. And you said values and I used? Yeah. Oh, no. But and your post handler. Yeah. In the post handler. Handle set over there. Yeah. Oh, no. You just passed the whole payload. Okay. Never mind. Yeah. Okay. Let me let me try. Yeah. I think I messed up the parameter thing. No. I was I was looking at the wrong I was looking at this one.
1:15:30 So async, we get a request and then it's using destruction over there. And it'll set and then we grab the key. That's weird. Let me try what happens if I curl the get endpoint at foo. That works. Okay. So if I don't do that, what is it? Is it is it It's a request. Come on that. That's that looks wrong to me. That looks wrong to me. Ah, come on. It's it's telling me what to do. Right? So I wanna get the request. So if I then args and I'm expecting it exactly this way, it looks correct.
1:16:34 Let me try. Let me try once again. Yeah. I think it's me messing up right now the syntax of the ET router. I'm posting some value to API values for error. Destructured parameter is undefined. So this has to happen over there. Let's call in extras and let's pass in extras. Let's do the JavaScript trick over here. Extras is any and let's say console. Log json oops. This one, and then let's do json's stringify extras. Sometimes you just got to do it. It's just I'm not seeing it right now and I think this is the easiest way to get
1:17:35 some hint on what I hold wrong. Okay. So obviously Oh, now we get a bad request. Okay. And undefined. That's interesting. That's interesting. We are so okay. Let me pull well, that's not me pulling. Let's look at enterprise. So I created a repository with like common patterns and how to implement them in spin with different languages. And there is an http crud and I just have to look up this syntax. Oh, it's the other way around. You see? Okay. That's a pity. Okay. I have to memorize that. Maybe it's too late today, but let's fix that and get our app
1:18:33 in a in a working state. It is seven almost 07:30PM for you. Right? I mean, it's been okay. Yeah. Good call. So I'm I'm hurry hurrying up right now. I'm trying to find the right Versus Code instance right now. This is it. Okay. So it's the other way around. Sorry for that. It's key. Yeah. It worked in the get handler. So obviously, it's the other way around. Right? Explaining the error makes it so obvious to see it works here. So it had to be the other way around. Okay. So we can remove our little helper
1:19:10 over here again and say this is key and we expect that to be a string. So now we should be back to a valid TypeScript. Yep. Let's go there, cycle through the workflow once again. So app is running and let's look for the post request. Now request is undefined. This is super weird. What's happening over here? Post item. It looks It looks pretty much the same, doesn't it? Yeah. Yep. I'm prepared. Let me check. Let me open this one. Oh, I'm not pulling, I'm not pulling things over there. Okay. I don't see the error for the
1:20:29 CRUD right now. Maybe given the the time and given how how long we are already in the stream. But but give me a sec. Key is set. Request is not set. And it's complaining about that API post. So there is a fundamental error somewhere. I don't see it. I don't see it. Hover over the dot post, does it give us the signature for the handler? Oh, yeah. It's a request handler. I request that takes an array. Oh, no. Can I can I simply pull it by over here? So that's a re any yeah. It could
1:21:18 handlers. Okay. Request any. I did that like a trillion times. I'm not seeing it. Let me check. Post items like this. Params. Oh, yeah. It's here. It's not key. It's not immediately. It's like this. It's params. Maybe it's like that. But that's weird. That means this should could also not work. Let me try. Let me try one more time. If not, we move on. Spin up build. And there we are. So curling the get endpoint gives me a not found. Oh, let's do Yeah. Let's try the post endpoint over there. Request is undefined. That's so weird.
1:22:32 Am I posting to API values? Yeah. That's correct. I was super confident this being the the signature. Like that. I have to try that once more because that was almost almost what I had in the first place. Besides, yeah, parameters. I I bet that's working right now because parameters make sense. Otherwise, my key could infer whatever's in that extras object. Let me try. So Oh, come on. That's super weird. Now it's happy again with that but not happy with my params. I better had it working like a day. Oh, let me check. So where am I? In my demos folder. Let
1:23:40 me check. My IP, it's CDKV. This is CDKV two. All Rust samples. This looks pretty close to what we had, right? Besides that the parameter over here is called ID instead of key. Okay. So I'm pretty confident that's exactly what we had. But I love to be proven wrong and this thread not working. How how to address that would basically e d e d router basically look at getting star registering routes over there and they are pulling this. That's correct. And it's just a question of pulling not just the parameter, also complete example pulling the
1:25:06 request in. But I had that. I think that's exactly the same as we had over there. Yeah. Oh, Yeah. Maybe we have to tell. Maybe we have to tell, hey, you should use the request. Yeah. That's right. Router fetch request or router handler. Yeah. I'm pretty confident that should be the right So it looks like you just passed the request, and then we used request dot text to get the array buffer. So if you just get Let me yeah. Okay. But let's So there's no function because there's no request dot array buffer. It's request dot text
1:25:59 according to one of the examples I found online. Mhmm. But I I don't know if that's correct. Okay. Let's do one thing to double check that. So if we lock the key for the get and for, I think, the post action that basically it it it throws over here already in that function that I'm calling. Right? So if I do that and if I do a console log request, so like this, and I guess it makes sense to JSON stringify both, and to see what's on the object. Stringify. Okay. So, there. F1. Okay. So it's running. So let's do a
1:26:59 curl to this one. So we get a not found and we expect now full being locked to the terminal. Okay. Meaning this works, pulling the key like that. Okay. Next is calling the post endpoint and we expect the key to be locked first and the request to be locked second. Okay. Curling the post endpoint, we get that error and we see foo. Great. Okay. So foo, it could either be that this is key. You know, it could also be the case that, right, not a second argument was logging and the request request being this. So let's just for before you run this
1:27:49 again to save as a step oh, I'm sorry. I guess, why not change key to params and just dump out the whole first object as well? The the whole This one? Yeah. Yeah. It makes sense. Params params and dump params, and pass in params. Let's pretend there is a key. Yeah, yeah. Okay. Yeah, that's smarter. All right, and let's curl it again. Oh, cyclic object. Is now undefined. Params undefined. So this was correct. This was key. If we go back to key But how can it be undefined and resolved to key? No, that was just key
1:28:49 being that string. Okay. Let's try again. I don't see how that could work. Yeah. You're oh, because yeah. It's an array. Right? And I'm destructuring now. Is your quest and key just in the same object? Oh, you mean like this? Yeah. Like this. Yeah. Is it that simple? Is it that simple? Yeah. Yeah. Okay. Let's Did you build it? Alright. Yeah. Have to clear that. Destruction parameters on defined. Okay. So it should be up and running. It is. Let's try. ArrayBufferRequest is undefined. That's super weird. All right. Maybe we just skip the post stuff and then Yeah. Yeah. Maybe it's worth
1:29:48 pulling like the working example there. Right? Which is leading to, okay, there is a post endpoint like personalize over here that's using like a popular NPM module from the community which is lang change js. So the idea of this example is to basically give us a front end where we can enter like personal information about the potential customer and like a general product description and then leverage a large language model to come up with a personal product description to make that super engaging and the customer automatically wants to buy that product. That's the idea. As you can see, we use the router
1:30:45 and we pull the request array buffer that's get turned into a JSON object that you know, matches like my interface over there. So again, we have the product description, we have some fundamental information about the customer, first name, last name, age, gender, nationality, and recent purchases. Yeah. And then after checking everything, we load some configuration data. So this is basically, you know, adding like spin variables to like what we use from spin. And then we use OLAMA as a as a large language model, you know, through its lang chain package, computer prompt and basically use chaining to say, hey, dear large language
1:31:43 model come up with a personalized with a personalized product description whatever, for whatever I entered into the form. And it's following the same pattern, right? So I have an API and I have a front end. However, in that case, there's little more in the index HTML. There's also like a minified version of Bootstrap and some super simple JavaScript because all I do from the front end side is issuing a fetch call to my post endpoint exposed through the Spin app. All right. I think the interesting thing, interesting pieces besides, okay, adding lang chain using Olama,
1:32:36 the Olama LLM are in SpinTML. So we configure three application variables. Hey, where's my OLAMA service? OLAMA is open source server that can run different large language models. So where's that API? Which model do you wanna use? Because OLAMA can run different things. LAMA, LAMA free, Mistral, DeepSeq, whatever, right? And what temperature? So how how let's say how creative could the large language model become in generating a response according to my question. So the there are three variables and we use them to, for example, allow the API to do outbound HTTP to our OLAMA server.
1:33:33 So we have to explicitly whitelist that. But we also wanna tell the API to make the the app itself, the API configurable without us having to recompile again and again if things change. So we allow the very the API component to use the variables ola mappaseurl model temperature. In contrast, the front end neither has to do outbound HTTP because the API is served on the same origin nor does it have to know about the OLAMA API which model we wanna use. This is just information that is only accessible to the API component. So no matter what
1:34:25 the front end component does, it is not able to access those values. So I can find granular pin or assign values to components. And I think that's the most interesting part over here. Okay, so how to assign them? So the easiest way is you could specify environment variables with a prefix spin underscore variable and then those are picked up. But you can also load environment variables from things like HashiCorp Vault for example, or when deploying to Fermion Cloud, you can specify those variables during deployment time and they are securely stored encrypted at rest in Fermion Cloud.
1:35:14 In SpinCube, you could pull them from Kubernetes config maps, from Kubernetes secrets, so all the familiar places, right, are supported. Okay. So now, I'm, mentally doing crossing all the fingers that I have that this works. And I'm super confident it works but I don't know why the previous demo did not work. So I do a spin build, right, to compile everything. And let me cancel this one because I wanna run the app on the same port. Okay. So I can either go down the road and can say spin variable and specify it like that, or I can
1:36:00 inline specify the variables and I pre like wrote that one. So let me I hope my terminal my terminal does not support new line over there. So what I'm basically doing and I'm saying, hey, there's a spin variable called ollama base URL. Set their its value to this URL. And there is a value a variable called oLamaModel set that to DeepSeek r one latest. Cycling back to the spin TOML, so those are required. So the app won't start without me specifying them. For temperature, I defined a default value of o dot four. So I could override it, but I
1:36:47 don't have to specify it because there is already a value. Okay. So now let me execute that. Now click the link. This is my super like fancy, let's call it fancy UI over there. As I told you, I'm not a I'm not a UX CSS guy. So let me do first name, last name, male, age. Yes, I'm 42 years old, German. I recently bought like the super awesome hiking shoe. Oh, hiking, sorry. Hiking shoe 44 whatever. I bought a new iPhone Pro and the product description. Let's say we have the Nebulus Smart Speaker 3,000. Or let's say the no.
1:37:55 Is Bluetooth water resistant smart speaker. So, okay, let's say this is our product description. And right now I have the Olama. The Olama server is running on Linode. So I created like a small Linode, deployed OLAMA according to their guide, deployed DeepSeq. And right now, it's not it's not pre warmed, meaning this will take a couple of seconds. It's basically right now constructing prompt, calling into the OLAMA server. And then it's just waiting for the LLM to finish reasoning and giving us like a super awesome like product description for the Nebulus Smart Speaker 3,000. The thing
1:38:55 that I like about DeepSig is how they expose the reasoning. You know, deepsake, you know, there's a lot of discussions about it. I don't wanna go down that road. It's more like it makes me it makes it so easy to understand how the AI comes to this product description. Right? I'm a 42 year old German guy who recently bought an iPhone Pro and some hiking shoes. So he's into tech, but also active outdoors. Right? So and and, you know, reading through that reasoning gives me also hints on how I could improve my product engineering or how
1:39:36 I could tailor it even more into a certain or particular direction. But I don't wanna go down that deep into AI. I think the super interesting thing why I've chosen this example is a, SpinApps are configurable from the outside. B, if you don't mess up the syntax of the router, like I did, you can with these create like Rust TRUD APIs and you can bring in like what's ever, you know, out there in the ecosystem and use it. So in case of JavaScript, I right now choose langchain the core framework and langchain the OLAMA LLM
1:40:30 configuration classes and functions they expose. They wanna have a abort controller that's not in the Starling run monkey runtime. So I looked what's the most popular polyfill for the airport controller in fetch and added that. Right. And my app does not look different from any other TypeScript app. Right? Besides, I mean, this one is not using the Spin SDK. So it's one to one looking like a regular TypeScript application. I can roll my own TypeScript files, modules, bring them in and use them as they are and have configuration values to make my app work in different
1:41:16 environments without having to change the code. Yeah, so that's basically the third example. And looking at that, I'm still super angry. And I don't see it. I don't see it. Let's don't worry about it. It's it's too late for you. I don't want Yeah. It is. Be back over here. So I'm gonna just recap that before we finish up. If anyone in the audience does have any questions, you've got about one minute to drop them in there, we'll do our best to answer them before we we'll outsource and go get these dinner. So Spend is an open
1:41:56 source project. It is now a CNCF sandbox project. It is a WebAssembly server side WebAssembly framework that allows you to write applications and HTTP endpoints and a whole bunch of other stuff. It focuses on a strong developer experience. As we've seen, we could do spend new to get an empty project, which give us, an environment where we can add as many polyglot components as we want. We can add Rust. We can add Go. We could add Python, and we've already seen that we can add TypeScript. These can be path based routing based on HTTP with the ability to pull in any
1:42:30 of the packages from the language runtime. We saw Thorsten pulling in lang chain, a very popular package right now within the NPM node, etcetera worlds. We've seen OpenTelemetry integration. We've seen OCI push and pull. And we've seen even the ability to use CRDs and deploy WebAssembly applications on top of Kubernetes. And all of that in one hour and forty two minutes. Bravo, Sorsen. That was a a fantastic, fantastic demo. Yeah. Thanks once again for having me. Sorry again for me messing up the second, demo, but still I think I think it's super ex we are in
1:43:13 a super exciting time, especially to, you know, with all the movements in the spin ecosystems, been going to CNCF and WebAssembly as a general tech evolving that that quickly. Super exciting. So thanks again for hosting this, David. And yep. Awesome. And thank you for, again, doing back to back streams, taking time out of your day, and, we hope that people that watch this enjoy it. Please remember to give us a thumbs up. There will be links in the description afterwards to the examples repository, the spin repository, and Thorsten's socials. So I think that's us for the day. And
1:43:52 thank you, Indigo. I appreciate the presentation and the perseverance. I love a bit of live debugging, and I hope that wasn't too stressful for Thorsten. So, yeah, I'm gonna let you go. I'm not gonna say anything else. I'm just gonna say thank you. I hope to see you at KubeCon in a couple of weeks. If anyone has at KubeCon, make sure to pop by the Project Pavilion where I assume Spend may or may not have a booth, but I assume will either way. So let's go say hello. Yeah. Definitely. Alright. Well, have a great day. Have a
1:44:21 good evening. Have a good week. I'll see you all next time. Thank you so much. Thank you. Bye bye.
Technologies featured
Meet the Cast
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments