About this video
What You'll Learn
- Build a full stack Spin app with a Bartholomew CMS frontend.
- Expose separate Rust and Grain API components for addition and subtraction.
- Run the app locally with spin up, then deploy it to Fermyon Cloud.
Building a full-stack WebAssembly application with Spin: a Bartholomew CMS frontend plus two API components, a Rust addition service and a Grain subtraction service, all running locally with spin up and deployed to Fermyon Cloud.
Jump to a chapter
- 0:44 Introduction: Spin and Full Stack WebAssembly
- 1:23 Defining "Full Stack" in this Context
- 2:08 Caveats: Code Quality and Prototyping Philosophy
- 5:05 Introducing the Calculator Application (Addition/Subtraction Services)
- 5:56 Project Directory Structure Overview
- 9:53 Running the Application Locally (`spin up`)
- 11:10 Exploring the Basic Frontend (Bartholomew CMS)
- 12:52 Testing the API Endpoints (curl Demo)
- 13:56 Deep Dive: Math Minus Service (Grain Code)
- 19:11 Deep Dive: Math Plus Service (Rust Code)
- 20:44 Frontend Demo with Calculator Form
- 21:57 Frontend Code (HTML/JavaScript) Explained
- 24:39 Future Plans: Integrating Modern JS Frameworks
- 25:15 Deploying to Fermion Cloud (`spin deploy`)
- 25:45 Conclusion, Recap & Next Steps
- 27:34 Farewell
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
0:44 Introduction: Spin and Full Stack WebAssembly
0:44 Hello, and welcome back to the Rawkode Academy. Today, I'm continuing my exploration of Spin. Spin is a microservice framework for web assembly applications from the team at Fermion. I've already done a quick exploration where I wrote my first spin service, and that was done two weeks ago on this channel, search for spin, and you'll find it. Feel free to go and watch that before following on with this one. This video, we're taking that initial application to the next level. What does a full stack spin app look like? So before we continue, we'll cover a few
1:23 Defining "Full Stack" in this Context
1:26 things. One, what do I mean by full stack? Well, I mean, we have a website that has been delivered by Fermion Cloud. It has one or more API available to it on the back end to do some random arbitrary calculation of some kind, And we can make the request from the website to the API, deliver results for the user. Typically, we're delivering a front end client to the user through the website. We have an API on the back end. Each API endpoint is going to be its own spin component. Now one more caveat on that list of caveats
2:06 I wanna give you is that please do not judge me for my code. So the purpose of today's demo is to show you what the structure of a multi component full stack spin application looks like. Not necessarily the best way to write a web for it or the best way to write the API endpoint and rust or grain. I'm still learning this stuff. So I'm not overly proud of the code. However, it's a proof of concept. I wanted to see what this looked like end to end and what that developer experience felt like. But I'm gonna show you all this. I
2:08 Caveats: Code Quality and Prototyping Philosophy
2:41 will publish the code to GitHub, and I'm I'm glad actually, I'm gonna continue to improve this. Now that I have something that works, I wanna, you know what what but I'm prototyping. Right? What I like to do is is make it work. Then I'll make it right, and then I'll make it fast. This is this three step adage that we've all heard as developers over the last ten years, twenty years, however long you've been doing this. I've made it work. I actually really wanna use Spin for some real applications. You may have seen a video on the
3:15 academy last year, but I actually broke down how all of the automation works for the YouTube Academy. And I wanna rebuild that in a cloud native way with bits of web assembly scattered through it. So I'm now wondering how do I loop this in my automation. And I'm gonna be rebuilding a lot of this. I'm gonna start to do this the right way. Spending a little bit more time not being ashamed of my code and shipping it. And then we'll talk about performance. I'm gonna do another video on Fairman Cloud in a couple of weeks where we look at what
3:41 the performance characteristics of this spend development life cycle and workflow and production deployments look like. But that's the caveat stunt. Not proud of the code. It does work. I'm gonna do it more. I'm gonna make it right. So I hope you're comfortable and happy with that as I am. I see we have a comment from Russell in the chat. Yeah. I do have a new video style. I decided to experiment a little bit. So I'm not in my new office yet. I move into my new office over the next couple of days. And as part of my new office,
4:14 I bought some new goodies. Some new green screens, some new lighting, a whole bunch of other things. None of that is set up today, but it did make me pull out all the stuff that I previously had to kinda do a bit of stock and inventory. And I decided to try and make this a little bit more interactive. And in fact, when we switch over to the screen share, I'm in the corner, we have the code. I mean, I I think there's some merit to this style, especially when it's just me this year. You know, I'm gonna try and focus on
4:45 getting the screen bigger and the text bigger and me being able to interact with that. I can't quite point to code yet, but, you know, as bigger green screens come, better lighting comes, opens up new options. So, yeah, I'm experimenting. Feel free to give me a thumbs up or a thumbs down, Russell and anyone else who's watching. Now, this video, we're gonna spend the next I'm not sure how long this be, twenty minutes, twenty five minutes. Taking a look at this application that I've built. A good thing that I'd like to build as part of a here's how to build
5:05 Introducing the Calculator Application (Addition/Subtraction Services)
5:16 distributed systems is a calculator, where each service within the calculator is an operation plus, minus, multiplication, division, cosine, etcetera. Today's is very simple. I've only implemented addition and subtraction very crudely, I may add. And I bet you're going there. How can you implement subtracting crudely? Okay. You're gonna see. There's one other service here which I haven't built yet, but as I go on and make this right, we are going to implement that. And I'll probably do that one live actually because I'm getting a lot more comfortable with the process of Build to Spin application. So let's just take a look. What we
5:56 Project Directory Structure Overview
5:57 have here is the calculator service. This is the one that's not built and the one that we will build together. This is the one that's gonna take some arbitrary equation from the user via the front end application and parse it. Build a build a bunch of tokens that describe what that equation looks like, so those can be divided into operations and sent off to the relevant microservices. Potentially, if we build the DAG of this, we can do this highly concurrent, which would be very, very cool. But that's the make it right part, not the part we're looking at today. So it's
6:32 currently empty. It's just the hello world rust application. But I do think we'll do this one together because I think it will be a whole lot of fun. We have config and content, modules, scripts, static, and templates. These are all part of Bartholomew. This is the content management and front end application. This is a river project. This is the main entry point for the user. It has a markdown rendering to the front end application. I've written some very good JavaScript to make the API endpoints to the back end. As I was doing this, I realized there's
7:11 actually nothing stopping me from yeah. I can use which has some cool characteristics, and I don't need to build like an NPM run build or anything like like a lit like a with Next. And because the web assembly module handles everything for our service. But honestly, if I don't want that and I'm not that tied to that approach, there's no reason that this application couldn't be an XGS application because works with Spin's file server component. So as long as I can do an NPM run build on an XGS application and get a disk folder, I can serve that
7:45 over the file server component as well. So depending on what you're looking to do, I started very simple today with Bartholomew and you'll see just what I mean. But I think the next version of this where we sit down and do this right, we'll see, okay. What is the status quo front end web development? Is it next? Is it SvelteKit? Is it something else? Remix? Astro? Let's use that. Build it into the build the pipeline, and actually deliver over the failed server component. And I think that'll be really interesting. So I'm looking forward to doing that.
8:18 Lastly, we have two services here in the middle, math minus and math plus. These are two individual services deployed as web assembly modules. Again, the code is not great, but one handles the subtraction. I don't know why didn't call it mass subtraction. One handles the subtraction operation and the other one handles the addition or some operation. So that's that's our structure. I think as I build more of these, this is gonna become my kind of my my standard layout and kind of put that web application, the user facing one first, top level. I guess I could nest it in some
8:53 way. Maybe I'll explore that when I do next JS stuff. And my only regret with this layout is that I didn't put math minus and plus into a services folder, but again, that would be really easy to do. And in fact, we could do that live on the stream if anyone even wants to see what that looks like and what I need to update to make that work, just say hey and we'll make that happen. So let's take a look at Bartholomew first. So I do have my application here. What I love about Bartholomew is that we
9:26 have a content directory, which does path based routing and markdown generation. Now I am going to delete all this code for a few minute. Hopefully not lose that on my paste buffer actually. Let's just do that. And say hello Spin and we'll just change this to be hello Rawkode live. So we'll save this. We're gonna come back over here to warp and I'm going to run a Spin up and a follow all, which will just give me the logs to my terminal. You've seen this on the previous spin video if you saw that. If not,
9:53 Running the Application Locally (`spin up`)
10:04 no problem. And we can see here each of the services that we have deployed. We've got Bartholomew available on Port 3000, which actually takes a wildcard group. It's gonna listen to all paths on slash that don't match any other path. We then have our static fail server as part of Bartholomew, which listens to slash static, also a wildcard. So anything after that will be delivered via the static fail system. This is the same module, which I think and but I know will deliver a Next. Js or any other front end application pretty nicely. We have the calculator which we're not using,
10:38 and then Math plus and Math minus. I've hidden them under an API endpoint with the Math plus and Math math parameter on the path. Now these are not sophisticated endpoints. I'm using the query URL to parse the the arguments that we passed to each operation. The reason I did it that way is when we look at the Grain code, you'll see I had a few challenges because I'd never written Grain before and not just my own inability to write Grain but the standard library is still very early with Grain. I focused on consistency of the parsing techniques rather than
11:10 Exploring the Basic Frontend (Bartholomew CMS)
11:14 in the Rust ecosystem jumping off, grabs and creates, doing some proper parsing. Was like, you know what? We'll just keep it crude on both and then it's gonna be very similar. So we can pop open our browser. You can see I've already tried to have local host on 3,000. And here we go. Let's zoom in. This is the standard Bartholomew theme and slots. We've got a sidebar. We've some length. We've got the kind of attributions here and then we've got Hello Rawkode live. We could make changes to this if we really wanted to. We can go into the
11:49 template. We can look at main and we can start to change this up. This is gonna feel very similar to Hugo. If you've used Hugo before and you come from the go background and the go ecosystem, we have templates, they have slots, we inject stuff into the slots, we deliver a website. That's it. Simple. Right? Cool. Now, I'm gonna bring back all of my horrendous code. And we're not gonna look at it just yet. Now, when we ran, spin up, oh, we've got our logs. We can follow along with everything that's happening too. Hey. Russell says, more suppose
12:32 I can't speak to you. More space for code, the better when you dive into it. Yeah. I I think so. I'm looking forward to, you know, I shouldn't really move it now, but, you know, been able to make this a bit bigger, pull it over my face and not lose too much, but there's still some tweaks that need to be happening. Cool. Yeah. So we also have these other routes. So what do those look like? Well, when we run spin up, we get everything. So we can pop open a new terminal and we can run curl v API.
12:52 Testing the API Endpoints (curl Demo)
13:04 Fast. Math minus, and we'll get a 500. Now my error handling is not great, has to be better, but I know because I wrote it that if we pass a question mark for a query string and two integers separated by a comma, worst API ever of course. But I didn't want to handle JSON parsing and Grain because guess what? There is no JSON parsing and Grain yet, which would make you wonder why did I use Grain? I was curious. Probably won't stay in it for much longer. However, if we do a five and two, we get a result of three.
13:45 So this is me actually printing JSON from the Grain language, hence the extremely weird formatting. With that being said, I think we should look at the Grain code. So let's pop open Math minus and we have our main dot GR. Now there's a lot to love about Grain and that is a truly WebAssembly native language and that the only target is WebAssembly. But it's a very early language as well. It's currently on 0.5 I think. There's lots of things just don't exist in the ecosystem but you know what? It was it was a whole lot of fun learning
13:56 Deep Dive: Math Minus Service (Grain Code)
14:26 the language and playing with it and I actually I'm really excited to where it's gonna be in six months and twelve months time. But not something I would ship to production. Not today. So borrows a lot from the JavaScript ecosystem and the Node. Js ecosystem and we have import statements which then have a from on the end. We can, if we wish, break this down and say that we only want the two list function, but based on the way that I have all the code below, we're importing the entire package so that we can call the functions and
14:59 properties of it. I define a record, we're not using the record, again, I was trying to marshal this to JSON, but it doesn't really exist yet, but it was fun to define a record. We just say record and we give it a name and then we describe it much like a JSON object in JavaScript, which I thought was quite nice. And even though it's a really early language, with Grain, we do get decent integration with Versus Code. So we can kinda see the same information here, but at least it shows me that an LSP is trying
15:29 its best. So maybe more is gonna come soon. Down here, we can actually see the Rx is an array of string, which we wouldn't normally know. So the LSP did very well there. And what I really liked was throwing around expect functions on results and options, something that we can do in Rust, but in the Grain language. So here I'm actually saying, we want to get the arguments. So we're using argv and this grain model or grain code, I'm not sure what we call it and if we don't get what we expect we're gonna fail with failed to parse arguments
16:05 and what we're trying to actually do here is pull out argv and we expect an array of string. So as long as we get some array of strings of arguments, we will pass the first check. We then wanna pull them out. I've just called this indices, but we're doing a turn this array into a list and then we're using the list semantics rather than the array semantics that probably could just have used array dot pop and then to list on that. However, I did the list dot drop which drops one element from the list. Next, I'm getting the equation,
16:40 which is not really what we're doing. However, it'll do for now. And all I'm doing here is saying that I wanna pull out the first value of the list. So I'm using list dot n. We this actually gives me something like this. Next, we do a string dot split on the comma and then array to list, we're pulling it back to a list and then we're using pars and on the nth element get out. So these two code boxes are the exact same. As you can tell, this is a lot harder than Grain than it would be in any other language, but
17:20 I felt committed at this point and I wanted to see what this looked like. Because when we use and build spin applications, we could use any language that compels the web assembly. And that's fun. Right? I could have used Swift, it'd probably been two lines of code. I could have used Rust, it would have been four or five lines of Could've used Java, Scala, Clojure, any JVM language, any dot net language, etcetera. However, I was committed to this point. So we're doing number dot parse in and we're doing an option expect where we expect to get some and to do back from
17:50 the center result expect wrapping it. Once we've done all the heavy lifting there of parsing two things on the command line or query string, we have to print the headers, which is content type application JSON and then I'm manually printing the JSON. Now the challenge and why this had to be three print statements is really funny. It's because I couldn't do interpolation or even concatenation here because there's no way to get a string from a number in green. At least I could not find out how to convert a number to a string. So instead of having a single print statement,
18:27 we had to have three and you can't print without printing a new line in green. At least I couldn't work it out, Which is why we have this very awkwardly looking JSON syntax. However, we don't need to save this. As you can tell from here, it works. We do do we do pass five comma two, we get a subtraction and the value of two or the difference depending on what you wanna call it. In the same vein, we can run math plus. And we can say five and two and we get result dot seven or result equals
19:03 seven and a proper JSON string with no arbitrary random new lines. And why is this? Well, because our plus function is implemented in Rust. Here, we are using the Spin SDK, which is quite nice. We are able to use a procedural macro around our function, which says this is the entry point or application that handles all the glue. We grab the URI query. We do a string split. We do a map, convert it to a u 64 n and then we sum it up. A lot nicer than the green approach. Still a little bit awkward.
19:11 Deep Dive: Math Plus Service (Rust Code)
19:40 I shouldn't really need to parse the query string. I should be able to pass in a JSON body, serialize that out. That would all work just fine but because I did that the grain way, that is what I went with. From here, we're just doing a JSON object of result sum and then spend it back out here with the response builder. Much nicer. This is easier code to test. I could extrapolate some of this to functions that I really wanted to handle the query string and the sums. But however, for a simple rust function and under 25
20:10 lines of code, this works really well. So we have two a two API endpoints that work. They do addition and subtraction, and we have a website. Well, let's take a look at the website. If I jump back over here and we just spin this back up again. And that's just because I was messing around with HTML and Bartholomew. There's no live hot reloading. I don't think so. Maybe that's a flag. But if we jump back to the web browser, we have this. So, hey, my lease just went out. Okay. We have this. I've got a form
20:44 Frontend Demo with Calculator Form
20:55 where we pass in number 10 and another number 76. And we have this drop down where we can see whether we want addition or subtraction. So let's do addition first and we hit submit and we get 86. If I swap this round and do five minus 12, we get minus seven. Now, when we build this right and we continue to, you know, make this distributed calculator a bit more useful. What I actually wanna be able to do is, you know, do the bot mass thing. Like, what happens if we have proper queries? And what's gonna happen is the calculate function
21:40 will parse all this, build a token of it, ship them off to the services, bring it back and spit out the result. That's where we're gonna be in a couple of weeks. But for today, you're getting 56 or fifty five fifty six minus 12. What does this look like from a code point of view? Well, this is pretty standard HTML and JavaScript. Although it's my version of standard, it's not a front end developer, so it's not exactly nice. However, inside of our index dot m d, we can embed script tags and other HTML tags. We're not
21:57 Frontend Code (HTML/JavaScript) Explained
22:12 constrained, but only markdown, which I thought was quite nice. So we define a really simple form, which has an idea form. We turn off the default submit handler so that we don't refresh the page. We do that with on submit equals return false. We have an input for property one and property two and then a select for the operation. That drop down should be between the values for your exabyte. I know it's not nice. I know. I know. We're gonna make it right just not in today's video. And then we have a submit function. I can't even spell results. So why don't
22:49 we fix that while we're here? And then we have this empty span and paragraph where we're gonna push the result from the response of the API. Next, we open up some script tags. We're just defining a function to submit the form. Now because this is just in the browser, Bartholomew as web assembly and that the server runs on web assembly, it generates the HTML, but the HTML still runs in your browser. So we have access to all the browser APIs, which means we can do a fetch call. We use the fetch API and we're just
23:22 doing some local run to slash API slash math with a horrible operator to flip the operation from plus to minus. Of course, we need to make that better. Yes, Russell. It is tech debt. Hold on. Let me pop that on the screen. Wanna hide your critique. I'll try not to make it too small. There we go. It is tech debt, but we're gonna make it right. So we send this off. We say that this is a JSON response by using the first promise then we pull it out here. So I've got some really crude console dot logs, make
23:57 sure that my thing was working. And then we're using result dot enter dot enter HTML, set the data as a result. Next, we just hook up the form listener. So we're grabbing the form by the ID and adding the event listener. It was when I was putting all this HTML and JavaScript together that I realized we have really good frameworks for this. And if we wanna build interactive client side applications, I would probably use SvelteKit or I would use Next or Astro or Remix. I think that's the four ones that I played with and like to some degree.
24:29 At least they make my JavaScript not terrible. And I was like, well, I wonder if I can deliver that like this. So that's my next improvement. And in fact, the next time we do the stream, we'll be hooking in an existing JavaScript framework with a build pipeline and having that deployed with spend fail server API or web assembly module. So that's the next step. Then we build the calculate service. And then we'll do that live. We'll do that. We'll have some fun. But this works. This is a working full stack application. And I'm really happy with this. The code sucks,
24:39 Future Plans: Integrating Modern JS Frameworks
25:05 but I think it shows the convenience that Spin can provide. I get the same developer experience. And you'll see here, this is me doing a spin up. When I'm happy with that, I can just run a spin deploy. And if we give that ten seconds or so, in fact, there we go, we now have this endpoint. If I just click this, we have my application posted on Fermion Cloud where I can do a 12 to minus result. And that makes me happy. So that is a full stack spin application with a pretty nice developer experience,
25:45 Conclusion, Recap & Next Steps
25:52 very simple production, with some notable rooms for improvement. So I hope you like what you see. I'll give you thirty seconds to throw any questions into the comment. But the next steps for this proof of concept are bringing in hold on. I hate my green screen. Bringing in a JavaScript framework. I think I'm gonna use Astro. It's the one I've been using to build out the academy website. So I I wanna see how what I can do with it. And it also does very nice static state generation very similar to Bartholomew. I think where Bartholomew shines us in the
26:27 same way that Hugo shines. Right? If you don't have a lot of heavy JavaScript client side logic, you just wanna take markdown content and render it as a a website, a static website for documentation and other needs, our volume is gonna be great. But when we want to start interacting with more spend components and services as API endpoints, I think let's just start to leverage all of this. That's step one. And then step two, we build the calculator. Alright. So thank you, Russell. Looking forward to the CDs. I'm really enjoying playing with Spin. So, you know, I can already see lots of
27:10 use cases for it. And I I love WebAssembly anyway. And so I'm gonna experiment more more languages. I think we'll bring in a little bit of Swift maybe for the calculate method. I'm not sure. Maybe some Scala. I'm feeling a little bit brave. But, yeah, I won't forget the logging and I'm glad you're enjoying the series. Alright. Well, thank you all for tuning in. Hopefully, that gives you an understanding of what a full stack spin application looks like. I think the structure works really well. I'm now in a position where we could start to lay it on more and more services.
27:34 Farewell
27:45 We can use local based routing with the services, which is nice. You know, the web browser just had to call slash API. We're gonna layer on more of this. And then when we're finished the calculator, I think we'll time about something real for my real Rawkode Academy automation. So thank you for tuning in. I'll see you all soon. Have a wonderful day. Adios.
Technologies featured
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments