Overview

About this video

What You'll Learn

  1. Build a Suborbital host-side GraphQL client in Go that can call external APIs from Wasm modules.
  2. Implement the Go-Rust FFI memory contract for request serialization, response lengths, and error propagation.
  3. Handle GraphQL errors and data parsing while running a Rust Wasm module against the host-invoked API response.

Connor Hicks and Francis Gulotta join David to add a GraphQL host capability to Suborbital's Reactor, writing the Go-side client and exposing it to a Rust Wasm module over the FFI boundary so sandboxed modules can call GraphQL APIs.

Chapters

Jump to a chapter

  1. 0:00 Holding Screen
  2. 0:40 Introductions
  3. 0:48 Introduction and Housekeeping
  4. 1:31 Guest Introductions
  5. 3:14 Project Goal: WebAssembly GraphQL Client
  6. 4:13 WebAssembly Sandboxing and Challenges
  7. 5:15 WASI and Networking Capabilities
  8. 7:00 What is Suborbital?
  9. 7:11 Platform Overview (Suborbital: Reactor, Atmo)
  10. 9:24 Shopify's Wasm Use Case (Scripts)
  11. 11:30 Starting the Implementation: Host Side (Go)
  12. 11:45 Live Coding a GraphQL WebAssembly (WASM) Client
  13. 11:50 Three Layers of the Build
  14. 12:49 Understanding the GraphQL Request/Response Structure
  15. 17:16 Host-Module Communication Mechanics (FFI, Memory)
  16. 23:03 Mid-Stream Q&A (TinyGo, Memory Access, Trust)
  17. 29:39 Designing Go GraphQL Client Data Structures
  18. 40:45 Implementing the Go GraphQL Client Logic
  19. 1:03:00 Handling GraphQL Errors in the Go Client
  20. 1:08:38 Committing the Go Client Work
  21. 1:09:38 Exposing the Go Client as a Host Capability
  22. 1:11:08 Defining the Go Host Function Interface
  23. 1:12:48 Discussion: Passing Complex Data (Variables, JSON)
  24. 1:15:30 Reading Data from Wasm Memory on the Host
  25. 1:17:40 Calling the Go GraphQL Client from the Host Function
  26. 1:18:30 Host Side Error Handling (Temporary Logging)
  27. 1:19:38 Host Function Return Value (Size or Error Code)
  28. 1:20:40 Writing the Response Back to Wasm Memory
  29. 1:21:58 Creating the Go Host Function Wrapper
  30. 1:23:11 Q&A: Wasm Use Cases & Garbage Collection
  31. 1:27:58 Switching to the Wasm Module Side (Rust)
  32. 1:28:15 Defining the Host Function Import in Rust
  33. 1:28:50 Creating the User-Facing Rust Function
  34. 1:30:15 Preparing Data for the Host Call in Rust
  35. 1:32:25 Calling the Go Host Function from Rust
  36. 1:35:42 Reading the Response from Wasm Memory in Rust
  37. 1:36:30 Setting up the Rust Wasm Test Module
  38. 1:38:20 Implementing the Rust Module Logic
  39. 1:41:38 Debugging and Compilation Issues
  40. 1:53:51 Successful Wasm Module Compilation
  41. 1:55:58 Running the Rust Wasm Module
  42. 1:56:26 Demonstration of Successful Execution
  43. 1:57:14 Summary and Concluding Thoughts
  44. 1:59:17 Final Remarks and Sign Off
Transcript

Full transcript

Generated from the English captions. Timestamps jump the player to that moment.

Read the full transcript

0:48 Introduction and Housekeeping

0:48 Hello, and welcome to Rawkode Live. Today's episode, we are going to be hacking and building a GraphQL client and a WebAssembly with Rust. Now before we get started on that, there is just a little bit of housekeeping. If you have not subscribed to the YouTube channel, please do so now. You can click subscribe, click the bell, and you will get notifications for all new episodes of Rawkode live where we'll be exploring the cloud native landscape and providing learning materials for us all to get through this mess together. Also, if you wanna chat and like to

1:20 chat, there is a Discord channel available at Rawkode.chat. Come and join us and say hello. We talk about cloud native Kubernetes and all things in between. That is it. Nice and easy. Now in order to help us on our journey today, I am joined by Connor and Francis. Hello, both. How are you today? Doing good. Well, thank you for for joining me. Do you want me to start with a little bit of an introduction? We'll start with you, Connor, just because you're next to me, and then Francis, if you wanna take over and tell us a little bit about yourself.

1:31 Guest Introductions

1:51 Yeah. I switched headphones last second there. Can you still hear me alright? Yes. We can hear you all good. Okay. Great. Yeah. So my name is Connor. I I created the Suborbital family of open source projects. I I've been a Go developer for a long time, but, for the last year and a half or two years, I've been delving into the world of WebAssembly, trying to build, cloud native specifically around WebAssembly. And so the Suborbital family of projects is my way of, you know, building the the ecosystem around WebAssembly that I, you know, wanna use as a developer

2:26 myself. My name is Francis. I've been a JavaScript developer for, I don't know, ten some odd years. And I've been making a lot of serverless web apps for professionally for a while now. And so recently, I joined Shopify to work on a WebAssembly platform, which is a lot of overlap with Suborbital, and that's how I met Connor. And I'm a big fan of GraphQL in serverless environments. Awesome. Thank you both for sharing. Yeah. I've gotta say, I'm really excited too. Like, I I'm gonna be honest. I have no idea what we're doing today. But whenever

3:08 I hear WebAssembly, GraphQL, Rust, I'm immediately sold and just wanna start having some fun. So, Connor, you had this this idea. Do you wanna give us a little bit of a context on what we're gonna do? Yeah. For sure. So with the, you know, Suborbital open source libraries, one of the things that people have been requesting of me since the projects have been out in public is the ability to make calls from a WebAssembly module running inside of reactor or Atmo, our two main projects, out to a GraphQL endpoint to be able to fetch data from an API,

3:14 Project Goal: WebAssembly GraphQL Client

3:45 like GitHub's API or Shopify's API or or whatever. So I thought, you know what? I I've done a little bit of GraphQL in the past. I'm not the most, you know, experienced person with it. So why not, you know, go a little out of my comfort zone and try to build this thing live on the air and and try to get our WebAssembly modules talking to a GraphQL back end and see what happens. Now for people who may not be aware, why isn't this as easy as just getting an HTTP client and and using it?

4:13 WebAssembly Sandboxing and Challenges

4:20 Yeah. So things get a little tricky when trying to have a WebAssembly module that communicates with the outside world because there's a sandbox around that module that means that, you know, the the module is not allowed to just do whatever it wants. The the host system has to actually grant it, you know, the ability to to do anything, really. So we have to allow these modules to access the outside world in a safe way by actually defining these capabilities on the host side and then making them available to the module. It sounds tricky. Yeah. And it it sounds like maybe there's not

5:03 a standard there yet, which means, like, that we can't, like, say, oh, yeah. Turn on network, and then now they can now it can make these GraphQL calls. Or is that not the or or or is there a reason why we don't wanna do that? So there is an up and coming standard that's being adopted more and more in the industry called WASI, which is the WebAssembly system interface, and more and more capabilities are being added to that all the time. But, you know, not everything's available just yet. It's not fully comprehensive. And so, you know, we're

5:15 WASI and Networking Capabilities

5:34 gonna try to build something that would maybe end up in Wazee one day, this this interface that we're gonna build today. Yeah. Though, I think if I remember correctly, the last time I looked at Wazi, they didn't actually have BSD socket share. Right? There's there's no networking. Yeah. There's there's some proposals in flight that are currently working on adding all of that to Wazi, but not quite yet. You just like making things difficult for yourself. Right? And, do we want a full network? Like like like, is that is that what we want to expose for this GraphQL project

6:06 today? We don't. We want the host to be able to have full control over what these modules are allowed to do. So the sandbox that I talked about before, it's designed to be denied by default. So, you know, if you just instantiate a WebAssembly module with absolutely no, you know, preconfigured capabilities, it can do nothing. It can't, you know, reach outside of itself in any way, shape, or form. We want the host to be able to say exactly what these modules are allowed to do and preferably be able to programmatically configure that when we're running our application. So

6:42 we're gonna try to make all of that happen today. So for our client, would it be, would it be an HTTP client that we were making, or would we wanna make something GraphQL specific? So, Reaktor already has a generic HTTP, client. I wanna create something that's very specific to GraphQL. Alright. Yeah. Okay. I I I think I understand what's happening here. Right? But because the WebAssembly doesn't have these parameters baked in, we're gonna be adding this to the reactor project, which is gonna expose hooks somehow to the WebAssembly to be able to make these calls.

7:11 Platform Overview (Suborbital: Reactor, Atmo)

7:22 Okay. And now That's right. For anyone that's not familiar with Suborbital, Atmore, Reactor, etcetera, I'm gonna show the website here, but can you give us the the overview? Absolutely. So Suborbital is a collection of open source projects, and, there are three that I refer to as the building blocks. So that includes Reaktor as well as Vector and Grav. Vector and Grav are networking libraries for Go, for HTTP servers and async communication. But Reaktor itself is a job scheduler, so it's designed to take tons of jobs as input, and then it has it'll create workers internally to efficiently

8:06 handle all of that work. And one of the ways that you can create a reactor worker is with a WebAssembly module. So that's how we're gonna go about it today. And then the you know, I call it the batteries included project is called Atmo, and this builds on top of those three building blocks to create a more user friendly version of, you know, WebAssembly as a cloud native platform. So it allows you to define full, you know, server API applications with a declarative file called the directive, and then it uses WebAssembly modules to actually handle the different things

8:44 that you described for that application. So we're gonna be working at the reactor level. So it's actually one level down from Atmo because that's where all of the WebAssembly internals happen. And then, hopefully, the the goal is that I'll be able to, you know, import all of that functionality functionality we build today into Atmo so that you'll be able to get the advantages of this GraphQL client in in either one, whichever you prefer to use. That sounds very familiar to what we're building at Shopify, which is actually one of the reasons why we know each other.

9:20 Yeah. So Do you wanna share a little bit about that as well? Yeah. So we have the Scripps platform. We just announced that at Unite a few days ago, which is Shopify's big developer conference. All that stuff's up on YouTube somewhere. Yeah. You'll be able to find it. Check this, I think shop shopify.eng, on Twitter. And we have this problem where, our stores get a lot of traffic, and we have third party apps. And they have their web servers, and they run, and they talk to our API. And that's great. Except when you get a lot of traffic, they

9:24 Shopify's Wasm Use Case (Scripts)

9:55 can fall over if if they're in the critical path. And so we have to keep them out of the critical path of, like, very performance things, like checkout. So we have this project, this this app called Scripts. You know, if you're a large merchant, you get to use it to customize your checkout. And it runs code on our servers during checkout. And we're able to do that by sandboxing using much older technologies than WebAssembly. Well, we have this new checkout, which we which we're talking we talked about at at Unite. It's significantly faster. We can do as

10:26 much traffic as we did on the entire platform during Black Friday, Cyber Monday, like, in one store, like, the on the new on the new checkout platform. And that's like oh, how was it? Over 200,000 checkouts just a minute or something like that. So we we want to make sure we can scale this customization part too and so enter WebAssembly. And so we wanna take scripts compiled into WebAssembly and execute those to customize our platform, you know, for your stores, you know, during checkout. And honestly, I wanna see a world where you write your app,

11:02 to run on Shopify's infrastructure, you know. And it's not you know, it's like our APIs are APIs, but, like like, WebAssembly can run anywhere. And it can run, as we've talked about, safely, you know, in sandbox. So so anyway, this there's just a lot of overlap here. And the the better the better the ecosystem and the better the tools we have, the better the tools we can provide, you know, and and capabilities we can add to our platform. So, anyway, that just makes me very happy. Awesome. Well, it sounds like you are both extremely qualified for today's task at hand, so

11:30 Starting the Implementation: Host Side (Go)

11:34 I am gonna sit back and watch. And I'll do my best to probe the questions as we go. Let's get started. What's what's what's step one here, Connor? So we're gonna start in the reactor code base, and there's actually three layers that we will need to concern ourselves with. There's the actual code to make the GraphQL request. That's step one. Step two is defining the interface that the host exposes to the module and making sure that the module can provide all of the information that it needs to provide. And then the third step will be

11:50 Three Layers of the Build

12:20 actually switching gears over into, Rust to actually consume that host API. So we're gonna add, you know, some capabilities to the Suborbital rust library that can actually call into that host function and take advantage of of what we've built. My Go is not any good. My Rust is okay. It's definitely We're all here to learn from each other. GraphQL APIs APIs for this. Right. Cool. So we're gonna start in the capabilities dot go file inside of the RT package if you wanna open your tree up there. Or you can oh, are you able to follow me? Oh, that's even better. Yes. It's

12:49 Understanding the GraphQL Request/Response Structure

13:11 been a while since I've used this fancy Versus Code plugin, so this is awesome. Alright. So right here is what we call the capability struct. So this is a collection of things that WebAssembly modules are allowed to utilize when they're running inside of Reactor. So as you can see, we've got a couple here. We've got a logger. We've got an HTTP client. We've got a file source to access host files, and then we've got a cache. So we're gonna be essentially adding to this list here, and we're gonna be adding a GraphQL provider to this set of capabilities.

13:53 So we can maybe start by taking a look at the simplest one, which is the logger so that we can you know? Actually, the logger is not a good example. Let's look at the HTTP client. The the logger just provides a logger object. That's not as fun. So it's really quite straightforward. The the the interface is just exposing one function, and that is, you know, take in an HTTP request and provide either an HTTP response or an error. And because Go has its entire, you know, networking stack completely built in, that amounts to one line, you know, to to call the

14:35 the HTTP built in clients. Right? But, you know, this part is only, you know, the it's actually the most boring part of what we're gonna do today. You know, making the GraphQL call from Go is is not actually gonna be the hard part. The hard part is going to be defining that interface between the host and the WebAssembly module. So, you know, this is going to be somewhat similar to what we build today to make a GraphQL call, but let's take a look at the you know, what that interface would look like so that we can get an idea of what

15:10 we're in for. So Can I talk a bit about what GraphQL expects? Yeah. How do we do that? Or we wanna talk at a lower level first? Yeah. If you wanna give maybe an overview of what GraphQL is is designed to be used for and and how maybe it's structured, that would be great. So people always get mad when I make this comparison, but have you ever used SOAP? You know, so if you have, you'll you'll understand. But, basically, there is an HTTP endpoint you can post to it. Sometimes you can get to it. GraphQL actually doesn't care how you get to

15:50 it. It wants a couple things. It wants a string that is a query. It wants a a hash that is your variables for that query, if any. It wants an operation name because your query can define multiple operations. And it's I was actually just looking at some other stuff. There are extensions. So some servers will take other inputs as well. There's a whole file upload extension, all sorts of things. But at the end of the day, it is a couple things in, mainly query variables and operation name. And then you get an object out. And they don't

16:27 even declare it has to be JSON. 99% of these things are JSON. I've seen seen message pack and other stuff. But but that has two keys, data and error. And so you get query variables operation name in and data and errors out. And and that is, like, the basic API. People took take this, put it over WebSockets, made GraphQL subscriptions. They've also taken this, put it in a GET request, and now you can have cacheable kinda, you know, GET, you know, for query only, you know, GET request kind of things. And there's all there's a whole slew of

17:02 extensions from Apollo and and a dozen other companies out there trying to trying to make a big in GraphQL. But the very basics, which is probably where we should start, are those are those three inputs and two outputs. Absolutely. So we're probably gonna be doing something fairly similar to this today, which is why I wanna kind of use it as the example. So this is a generic HTTP interface. So, you know, where we, you know, we started with that HTTP client, this is the actual Go implementation of the HTTP client, but then we need to define

17:16 Host-Module Communication Mechanics (FFI, Memory)

17:37 the you know, how the WebAssembly module will tell the host that it wants to make one of these requests. So we'll take a look at, you know, this portion of the reactor project. This is where we define that interface. So one of the quirks of WebAssembly is that you can't take a go variable and pass it and expect that Rust understands that go variable. You need to serialize any information that you wanna pass in between the module and the host so that, you know, they can understand each other. Now, again, WebAssembly is, is evolving, and so there are some standards

18:18 that are being put in place, to be able to actually natively share memory in the way that would be, you know, maybe a little bit more efficient and a little bit, easier to understand. But as it stands today, we're gonna be doing some serializing in order to pass information between. So in Reaktor, when you expose a host function to the WebAssembly module, we define it, and it looks something like this here. So the fetch URL host call takes in a whole list of parameters, and I'll kind of go through them each. So first, we ask, okay. What's the HTTP method

18:53 that we're gonna be doing? And that is a set of constants. You know, we either gonna do a a get or a post or a patch or delete, and those map to numbers one through four. So that's number one. Why? Why Why do they have Yeah. Because That'll take us right. Want to, yeah, we wanna minimize the amount of serializing and deserializing that we have to do there to be a little bit more efficient. So anything that we can map down to a simpler data type is usually a good idea. Okay. So there's no there's no restrictions on

19:31 on the kinds of data types, though. So if we really needed a string, we could we could get we could get that string, and I'm assuming that's what the URL how the URL pointer works. Kind of. So WebAssembly naturally does not have a string type. You can't define a string. But individual programming languages do have a string type. So what do we do there? How do we get a string to kind of traverse that boundary between the host and the module? And what we in order to do that, we write the string to a particular

20:06 piece of memory, and then we pass a pointer to that memory, you know, in either direction so that, you know, you know where to find it. So when the when the WebAssembly module asks the host to fetch a URL, it is going to include a pointer to the string in memory where it can find that information, and then it's gonna tell us the size of that memory as well so that when the host receives this function call, I'll I'll cover in a second how it can actually go and ask the module, hey. Give me this memory

20:39 at this pointer so that I can figure out what URL you were trying to tell me about. And then we do the same thing for the HTTP body. So it's it's these kind of pairs of of data types that we're gonna be passing in order to to accomplish this. It's a little bit kludgy, but, you know, the the advancement of the WebAssembly standard is gonna mean we don't necessarily have to do this anymore once, once things get a little bit more you know, a little bit further along. So Now can I guess this is completely

21:14 safe to point to arbitrary places in memory? Not always. So WebAssembly defines linear memory. So there is actually globally exported blocks of memory that the host is allowed to dig into and and grab. So we have to define a contract between the module and the host to say, you know, you you you should be looking at this piece of memory in order to safely, you know, communicate with me. You can't just go arbitrarily, you know, poking around in there. And so part of what Reactor does for you is kind of handles that all for you to

21:50 ensure that it's be being done safely, and and you're not just arbitrarily messing around in the in the memory of the WebAssembly module. Makes sense. Yeah. So in order to reconstruct that memory into a string, we have a couple of, you know, helper functions that are inside of Reactor. So there's this read memory helper function here that allows us to take that pointer and the, you know, the size variable and actually pull that information out of the WebAssembly module as it's running. So the the WebAssembly module exposes its global memory to us, and we can use that you know, the

22:30 pointer to access the data at a particular point, and then we can read the data to a certain endpoint. And then, you know, because of the interface we've defined, we're interpreting it as a string. So once we, you know, once we have read that memory out of the WebAssembly module, we convert it to a string to so that we can use it as a URL, which is the the thing that, you know, this function is actually attempting to do. Cool. Any any thoughts or questions from anyone at this point? So I've learned a lot in the last

23:03 Mid-Stream Q&A (TinyGo, Memory Access, Trust)

23:10 five years. I think it's really cool. So if I understand this correctly, we have to define a Go interface that kind of prescribes how the Wasm side of things knows how to make this request by enumerating values. Like, maybe it's a GraphQL query as a one, a GraphQL mutation as a two, and then we have the ability to use pointers and memory space to actually squeeze in the query and then execute it and then do the same in reverse to get the result back. It's kind of where I think we're headed. That's exactly right. So you you kind of

23:46 you got to the end pretty quickly there in that. What we're gonna end up doing is we are going to write the results back into the WebAssembly module's memory. Once we've completed the GraphQL request, we're gonna use this helper function here, which actually writes the the memory. And then then the module once the function call completes, the module will then go and read that memory to reconstitute the the results on the other side. So, you'll see this this, acronym FFI, throughout the code base, and FFI stands for foreign function interface. So when we talk about crossing that boundary

24:31 between the host and the module, we call that an FFI, a foreign function interface, so that it it's basically the the idea of how do we translate the function call across that boundary and make it so that it's a, you know, properly defined host call, essentially. So when you see that f five terminology host and module. Yeah. So if I if I had to take from context, host is the the the fully you know, the process is running on an operating system. It's the thing that executes the Wasm module. Is that is that correct? Yes. That's right. So Reaktor,

25:12 you know, when you when you use the Reaktor library and you run a Go program that uses the Reaktor library, that process is the host. And so because it's just a Go program, it has access to the whole wide world of anything, you know, network files, whatever it wants to do. And then the module, which is the WebAssembly program that we have compiled, gets loaded into Reaktor as a child or as a guest, and then that guest module is allowed to communicate with Reactor over this FFI that we define here. So this fetch URL function is just one of the

25:54 FFI functions available to the guest module. And you said something that that, and it looks like, actually, we have some questions in chat. But, but you said something that maybe is important, maybe it's not. You said when the request completes, we tell Wasm. Does this mean that there's some sort of asynchronous ex exposure to Wasm, like, from our Wasm code? So everything in WebAssembly is synchronous. There is not yet the concept of threads or asynchronous execution in WebAssembly. So this is all actually happening in sequence. So the the order in which things happen is reactor

26:36 loads the module and starts to execute it, then the module makes a function call to the host. The host performs some kind of some kind of action, like making an HTTP call, And then the host relinquishes control back to the the module where it can then receive the results of that function. And so our code in the module says, hey. Get me this GraphQL API, and it's like, oh, I got it. Thanks. You know, it just gets it synchronously. That's exactly right. And yeah. And and because WebAssembly doesn't support any kind of threading, Reaktor kind of helps you with that. Because Reaktor

27:16 is able to run dozens or hundreds or thousands of WebAssembly modules all at the same time, you can actually almost achieve threading by just running many copies of that WebAssembly module all at the same time. So Reactor helps you do that by creating pools of workers to accomplish different tasks in a multithreaded way. Alright. Can we tackle a couple of questions from Reza, and then we can get into some Do it. Coding here. So Reza asked, are you using tiny Go or Go? So for the host, for Reaktor itself, that is written in Go proper.

27:56 And if you wanted to run Go code as one of the guest modules as an actual WebAssembly module, that's when you would probably reach for TinyGo because TinyGo has a really good WebAssembly tool chain, that is quite honestly better than the regular Go tool chain in in a lot of ways. And there's a a follow-up that says, how do you know which memory space needs to be accessed? Are there any tools? Yeah. So, technically, WebAssembly modules can export multiple memories. It's an interesting concept that I haven't fully taken advantage of yet, honestly, but there is kind of a main memory,

28:38 a global memory that is exported by every WebAssembly module. And so for Reaktor's case, at least, we only use that main memory module. But maybe in the future, I'll find some use case for having multiple kind of individual memory exports. Does does this mean it can export its memory safely? As in, like, if it doesn't trust the the host? Yeah. So it's it's usually, the the trust relationship is in the other direction. So, usually, it's the host mistrusting the module. There is, you know, not it's not really designed for a module to mist mistrust the

29:19 host. I'm sure there's ways you could do that. Smarter folks than myself would need to figure out a way to do that properly. Alright. Well, I think, Reza, you'll hopefully, as we dive into this and write a little bit of code that the pointers and size will become a little bit more apparent to you. So let's let's move forward and get some code work. Yeah. Alright. Actually, I think I got an answer there. I believe there's just a fixed pointer size in in WASM. Is that right? There's a fixed memory space and a fixed pointer size

29:39 Designing Go GraphQL Client Data Structures

29:51 that everybody needs use. Yeah. So the the fun part about WebAssembly is that so I mentioned that it's linear memory. So memory in a WebAssembly module is essentially just a gigantic array, and you can access that array at different points. And so when you pass a pointer from a WebAssembly module to the host, you're basically just telling the host, hey. Access my gigantic array at this particular index and read it for this number of bytes. And so it's it's actually pretty cool. Great. Alright. Oh, and I guess we should take a quick look at the the host code first

30:28 before we move on. So I wanna show kind of the matching the matching WebAssembly internal call. So we looked at the, you know, fetch URL call on the go side. But if we switch over to the Rust side of things, this is going to be the code that gets actually compiled and put inside of the WebAssembly module. So in the Suborbital Rust library, we define, you know, an HTTP module. And inside of that module, we actually define an external function with the exact same signature as the host function. So when we are inside of the module,

31:11 we can call this function, and it will directly map to the host's version of this function. So when this code calls fetch URL, what it's really doing is asking the host to run this host version of itself. So that's how that communication happens, and we'll probably dive into that more in a in a little while. But, yeah, we can we can choose either Rust or AssemblyScript. Whichever we wanna go with today, I'm I'm I'm cool with. My voice actually Rust. Yeah. I think that's the the more fun option for sure. Alright. So step one here to define the

31:58 host interface then? Yeah. That's right. So we're gonna create one of these capabilities that is just Go code to make a a GraphQL endpoint. So I'm gonna create a GraphQL dot go file, and we're gonna start from scratch. We're doing this from nothing. We we have written no code yet. So this lives in a package called r cap, which just stands for reactor capabilities, and we have to get started here. So we want to start with this do one of you wanna to to to to do this part? I'm happy to let one of you type.

32:41 I think I think, like, just to like, deciding the inputs and the outputs might be an interesting thing. Because I know how we would do it, like, from Rust. If I were to make if I were to have a Rust function, right, a method that that that, like, takes GraphQL input and output, you know, I I would I know what that I want that to look like, and I'm curious how to translate that to our host guest you know, our host module interface. Cool. So why why don't we sketch it out in pseudocode here, and then we can

33:13 actually get to writing the proper Go code? I'm wondering if this is, if you're seeing this. I got I got Copilot running on my machine. No. You're not seeing this, but it's really crazy. The stuff they're trying to do for me. Okay. So I I know where you have a query. I know a query is a string. Right? Yep. And I know we're gonna have variables. And I know variables is going to be it is gonna be a object, a hash. So this is gonna be like a JSON object. Really though, like, everything altogether and and we

33:53 have an optional operation name, which is also a string. And I guess variables is also optional. So, like, this is my TypeScript showing. But the whole thing actually is gonna get to JSON. And 99% of the GraphQL servers out there. So so maybe our only input is a string. And I don't know. So I think the only thing we're missing here is the endpoint. I need to know where this thing is going. Yeah. Now one of the questions will be, do we want yeah. Yeah. You're right. So the the question is, do we want to instantiate

34:36 the object with a predefined endpoint, or do we want the module to be able to pass it, you know, whichever endpoint it wants when we make the the host call? We don't have to decide now. Cool. So we're going to wanna create some a Go struct to kind of help us along here. So we'll start with GraphQL client stretch, and we're gonna it's gonna wanna have an HTTP client as, you know, one of its internal fields. But other than that, I mean, there's not a whole lot that we need to add here. So we're gonna make sure we comment our code

35:16 well. Is a GraphQL capability? I can't type for reactor modules. Great. So we're gonna oh, yeah. That's right. So date errors are as an array. Right? Okay. You're way ahead of me. Yeah. Awesome. Sorry. So I think the first thing that I would do when I'm approaching this is I would, you know, take you know, we we've defined pretty well what our input is here, and we should define what it is we're sending to the the GraphQL server. So I usually start with a, you know, a request type. So if we define a GraphQL request,

36:04 type that includes our query, which is a string, and that goes out to JSON of the string. And we include our variables, which is a, in in Go, it's called a map, so a map of strings to strings, and that will go into JSON as variables, I believe, if I'm not mistaken. And then that operation name operation name is a string, and that goes to JSON as operation name with that capitalization, I think. Yep. And since it's optional, we're gonna add this omit empty directive here so that if we don't provide one, it's just not gonna include it in that

36:49 JSON output. Variables to is you can omit empty. Oh, yes. You're right. So keep our code nice and well commented. Is a request to a GraphQL endpoint. Cool. So we've kind of defined now. We're gonna be sending this to the server, and now let's define what we're gonna get back. So a GraphQL response is looks something like this. So data is I'm assuming that's gonna be bytes. It's gonna be a JSON object or a null or JSON null. I see. Okay. So then I guess it would probably be a map string interface. That's usually how I define just

37:41 a some unknown JSON blob, and that is an actual key called data. Right? Yeah. And the response? Great. And then our errors are an array of the array of an object that looks like this. Is that right? Correct. Message path and type? Cool. So we'll probably wanna define that as its own, its own structure here. So if we define a GraphQL error and oops. I gotta tell you, it's not standard. The the message in the path is standard. Every server gives a little extra. Gotcha. Cool. So we'll define what that GraphQL error looks like, and we'll define,

38:35 the path JSON path. And then we'll say that the errors is an array of GraphQL. Man, I need Copilot to give me better better suggestions here. You can thank Copilot for the definition of errors there. You just did it. Nice. Alright. Oh, there's no there's no colon there. Okay. How's that look? Am I missing anything? No. The former architect then, so I can get That's what I know if my code works. That's right. One of the for anybody who's not familiar with writing Go, there's a tool called GoFmt or Go FMT that runs every time you save a file,

39:24 and it will apply a standard set of formatting to your code. So, for example, all the spacing here, lining up these these, you know, back ticks so that they look pretty, that's all done automatically for you. So it makes you, you know, it makes your code even prettier than what you're what I'm usually, willing to put effort in for. Alright. So I think just I I'm not sure if data and errors are both always there at all times. I'm looking up the response format, like, spec right now. But if that doesn't really matter as much, we can figure that out later.

40:01 Yeah. It's it's never a bad idea to include Omid empty. There is no, no harm in in doing that. Alright. Let's keep our code commented. It's a GraphQL response. Yes. Data should, According to the spec data, it should always be there, but sometimes it's null. If there were errors, it'll be null. Gotcha. Alright. Got a comment from null saying we have a typo and our message JSON definition. Message JSON. Ah, you are correct. Good catch. That would've that would've been real annoying when it goes to unmarshall the JSON, and we find that our message is always empty.

40:42 Good catch. Good catch. Alright. So those are inputs and outputs. So now we need to actually, you know, define that function that will do it for us. So we're gonna create a function that operates on the GraphQL client, and the HTTP client calls it do. So I'm just gonna call it do as well. And so this is where I'm maybe gonna ask Francis what the best, you know, the best practice is here. Do we want to have query and mutation defined as specific types, or what do you think is the best thing to do there? No.

40:45 Implementing the Go GraphQL Client Logic

41:22 So your your query document can include mutations and queries and, you know, anything else you wanna your server might support. So so I think the difference, in the in the result is is nothing. The difference in the input is nothing. So I I don't see them as being different method. I see. So you actually you actually specify that inside of your query string. Is that right? Exactly. Cool. So then I guess we'll just start with of you'll find a lot of and I I don't know if this translates for us. In, like, the React world, all the hooks, you'll

41:57 find different, like, methods for query and mutation. Only because queries you probably wanna execute right away, where mutations you wanna choose to execute later. Like, you wanna set them up and then use them. And that might be the only reason we wanna do that, but I I don't I don't see why this interface would would would include that kind of concept. I didn't know that. That's really useful. I thought that you have to send queries over a GET and, like, mutations over a post. That's just what I've seen in other libraries, but today I've learned seen different

42:27 things in different places. Under the hood, mutations are actually a mutation in the input and then a query. And so it's actually, like like, it's like, at the end of the day, they're all the output is always a query. You know? So it's the same kinda same operation in GraphQL. Oh, Reza pointed out another typo here. This is not gonna be called string in the JSON. This is gonna be called query. Alright. The audience are confused. Right? My fingers yeah. Sometimes my fingers work faster than my brain. We need the high and high level code.

43:03 Exactly. Alright. So this function is gonna either return a GraphQL, response, or it's going to return a GraphQL error. Now in Golang, I usually don't like to return a concrete struct type as an error. So we're gonna return the generic error type, and we're gonna actually make GraphQL error conform to that type by just giving it the one method that it means. So In which case the graph our graph will response doesn't need to include errors, or does it? Right? Good point. I think it does. Actually returning that directly. No. I think you're right. So

43:45 what what I would what I would tend to do is that if this is populated in the response, we would just, return an error here and tell them to look inside of the errors array. So you you make a good point. We're not returning this directly. We actually have an array of them inside here. So, yeah, that's that's a good point. I believe it's a choice of the two sometimes, if there is an error in execution, to include partial data as well. So a field can error, and you still get the other fields. I see.

44:12 And so I see. So you can still have, like, a usable response object. Okay. Cool. So, I guess, for now, let's include the endpoints, in the in the actual function parameters. We may choose to pull this out and make it specific to the client object later, but for now, let's just let you pass both, in one go. So for those of you who are like myself and are fairly new to GraphQL, it's it's really nice that GraphQL queries are really just HTTP requests. So we can get away with, you know, using Go's built in HTTP client to to accomplish a

44:55 lot of what we wanna do here today. So we're gonna build an HTTP request. We're going to send that HTTP request off to the endpoint that's defined here, and then we're gonna parse the the response. So to start, we're going to create that request object by using the HTTP package, and that has a new request method inside of it. So here is where that that spec ambiguity can maybe cause us a little bit of pain. So we need to choose our HTTP method here. And so I'm gonna Would it be simpler for us to assume

45:38 it's a post with no headers just to get it going and then add it in later? I think that's the right call. So we can start with, http dot method post here. And, yeah, like you said, that's the simplest way, and then maybe we can make it fancier later if we have time. So then we're gonna pass in that URL. So this is going to be our endpoint, and then we're gonna pass in our body. So we need to actually construct that body. In the in Go, we use this it it it accepts this IO dot reader type,

46:09 which is an interface that just, you know, expects you to be able to read data from it. So we need to build something that conforms to that interface. So in order to do that, we need to figure out what's gonna go in it. So, we're taking in, you know, the query string, which is going to be placed inside of this field in the GraphQL request. So we need to actually build this request object before we can kind of continue. So, let's create a request object that is of type GraphQL request, and we're gonna stick some data into it.

46:44 So the query is going to be the query that was passed into us here. And then, to start, I think our variables can be, empty. So we'll just create a map string to string that has no data. And then the operation name, I think it also optional. So, actually, we'll we'll exclude that because it has that omit empty operation on it. Technically, we exclude both of these, but, you know, just for illustration purposes. So then in Go, when we wanna take a a struct and create JSON from it, we use the JSON package. So we're gonna

47:18 call sorry. We're gonna say that the request bytes is JSON dot Marshall, which is the equivalent of JavaScript to JSON or JSON dot stringify. I can't even remember what it is at this point. And we're gonna pass in that request object. Yeah. And then we're going to check our errors here so that if, you know, the JSON was poorly formed, we could catch that before we try to send to the server. So this this errors dot wrap for anybody who's never seen this before. This is this is something pretty unique to go, I think, where we wanna

48:02 make sure that we have some indication of where this error originated. So what we're doing is we're taking the error that came out of this function call, and we're wrapping it into in an in a message saying where that, kind of failure came from. It just is, a habit that I've picked up. Whether or not it's standard practice, I don't know, but that's just, how I've always done things. This is this is actually a feature that's coming to JavaScript pretty soon too. I think probably because of Go. But just, like, adding that more context and keeping the

48:32 original error behind or having multiple errors that you wanna send as one. Like like, it's just a common pattern. And so I'm happy it's happy it's got language support here. Mhmm. For sure. Cool. So now we've got bytes, a variable containing bytes, which is our JSON. So we're going to as that last parameter in our, request builder, we're gonna use the, bytes dot new buffer, which is it takes those bytes and loads it into that IO dot reader, interface we were looking at before. So the rec bytes will go in there, and now we have a request.

49:06 So we're gonna do the same thing. We're gonna do a similar error checking for this call, but instead, we're gonna say fail to request. Request. And then, we have a request object. So we can take a quick look at what that looks like for anybody who's unfamiliar with Go. The HTTP request in Go is just a method and the URL that you're making that request to, a bunch of stuff that I don't care about, some headers for the HTTP request, a body, which will be the request body for our post request, and then, you know, some other metadata like

49:44 content length and some you know, if you want and wanted to use a a form request, you could do that, multipart, etcetera. So lots of fun stuff in there that you can take advantage of, but we're doing pretty a pretty basic, requests today. So we're really just gonna be including a, buffer that includes our request. And then we're gonna want to add a header. So just just to specify the content type, I believe that's standard practice, if I'm not if I'm not mistaken with GraphQL. So for the Application JSON. Dot header. Yeah. So we're gonna add a

50:22 content type. Now I've seen conflicting reports. I've seen application JSON, and I've seen application GraphQL. So do you wanna set me straight on what I should be using here? I mean, it's not in spec. It's whatever the server wants. Application JSON will work everywhere as far as I understand it, though. Gotcha. Because, like, most most clients are setting that because they're sending JSON. Understood. Understood. Alright. I think the application slash GraphQL is if you want to send the raw GraphQL video, right, without actually Oh. Change that to JSON. Just got Without wrapping it in in this

51:00 struct, you mean? Well, yeah. Like, you know, so, like, a GraphQL GraphQL request would be, like, query I can't remember the exact syntax, but all post name. Like, you can send it without JSON without converting that to JSON as GraphQL, depending on the server of ours. Got it. But we don't need to worry about that today. Let's just do JSON. Yeah. Fair enough. Alright. So then what we're gonna wanna do is we're gonna actually wanna make that request. So we're gonna do we're gonna take the, HTTP client that we added to our GraphQL client, and we're gonna actually,

51:38 use it to make a an actual request. So we're gonna do, g dot do. Oh, that's gonna be confusing. We should actually call this client to prevent making any mistakes. If we have a do request do function on this and a do function on the HTTP client, we could run into some issues here. So we'll do g dot client dot dude just to be very specific about what we're what we're trying to do here, and we're gonna pass in that HTTP request object that we created up here. So we'll do the same error checking right here.

52:14 We're going to return no and failed to do, and then we're gonna do something with this response. So that response contains hopefully, if everything goes correctly, that is gonna retain something that looks like this. So we want to actually take the raw response, and we wanna load that data into that struct. So we're gonna create, we'll call it GQL response, and it is going to be a, GraphQL, response object. And then we're going to use that same JSON package as before, But instead of marshal, which takes a struct and turns it into JSON, we're gonna do the opposite. We're gonna

52:58 use unmarshal, which takes JSON and turns it into a struct. So, we're going to unmarshal the, response dot body into the GQL response. And we're gonna be a good good best practice for Go is we're going to we're going to close that that body similarly to how you would wanna close a file. We're gonna close that body, when we're done with it. And we're gonna check the error on that as well. Of course. There's a lot of this this this this stanza. It repeats itself a lot in Go code. I've gotta say, I'm really impressed that you

53:43 haven't like, whenever I write Go code these days, there's 50% go and 50% rust, and I always have to go back and fix it. You've not typed f m once or That's me when I'm writing Rust code. I will very often accidentally write Go code while I'm writing Rust. Yeah. I was writing I just realized stop print or print l n in the wrong language all the time. It's so annoying. Yeah. I just realized we we missed a step. We need to we need to do response. We need to do response JSON is IU tilde read all

54:16 response dot body. We need to actually extract the raw bytes out of that first. I should really put this error not nil into a into some kind of snippet. Yeah. I've become co pilot. Use snippets. Use snippets. You're gonna have some copyrighted code in there. Alright. So now we have we've made the request. We have read the response out of out of here, and we're gonna actually do the right thing here. So we've we've read the we've read the raw bytes out of that response, and then we're taking the JSON package to actually convert that JSON

55:04 into the GraphQL response type. So if we've gotten this far and nothing go has gone wrong, a, I'll be shocked if it works on the first try, and b, it means that we're done. So we can actually return that GQL response and a nil error to say that we are done. So, yeah, Reza pointed out that h ttp dot new request is not available in TinyGo, and that's exactly correct. So because when we talked about earlier that WebAssembly doesn't have a native networking stack at all, Pretty much the native the native network library for any programming language

55:41 won't just work out of the box inside of WebAssembly. So that's why we're creating, you know, these interfaces so that we can basically ask the host to do these complicated networking tasks on behalf of the WebAssembly module instead of trying to do it from within the module itself. Alright. What is this complaining about? No custom. Oh, that's right. In order to in order to do proper JSON marshaling, we need to export all of these fields. That's something that I always forget. That was a a comment earlier from which I didn't understand, and now I do.

56:21 They told us our struct fields aren't exported. Yes. That's that's probably why it had the yellow squigglies before. Alright. Cool. So I think, we have got a very basic, GraphQL client here. So do we wanna try it out and see if it actually works? No. That's good. Let's finish. Alright. Well, it's really nice hanging out, though. Like, are you sure? You want him to stay in here? Let's let's do it. Let's let's see if this works. Well, do we have a GraphQL API out there that will take an unauthenticated post and give us some data?

57:02 I do. I think David. I think David might have something. Yeah. Cool. So I'm gonna quickly create a QL client. I'm gonna quickly create a constructor for us here, and then we can get to it. So we're gonna create that GraphQL client. We're gonna call, h t t dot default client, and then we're gonna return g, and that is going to be the GraphQL client we're looking for. So now we've got a constructor to actually create this client, and we can actually try to use it in graph. Why is GraphQL client so hard for me to type?

57:47 I'm not quite sure. Okay. So did one of you wanna try try actually using this thing? I like I like to create start? I like to create a tester file here with a main dot go, and I will I will create your I'll create your main function for you, and then I'm gonna let one of you two, go crazy and and It's gonna have to be you. I don't know. Go. David, I think you need to take a stab at this. I wish I was paying more attention, but sure. Let's go out there. Pokemon test servers are

58:27 the best. Thank you, Rizzo. Alright. So we wanna be able to consume this new GraphQL client. Right? Mhmm. That's right. So we should be able to import from the r cap package that so it'll be r cap dot new GraphQL client. Mhmm. So for Go, we're gonna need the fully qualified import path. Oh, yeah. Alright. Cap. Just like that. So that should be cool. And it doesn't return the matter. Yep. It just returns the thing. So now I can do Yeah. Find dot do. Yeah. And I can call my endpoint. Yeah. And I can pass in my

59:23 query, which I'm gonna jump over here and get one. But this is it. This is an API that I've been I'm I'm trying to move all of the episode data and stuff like that to get hub and then publish this API so that it can be consumed by anyone. So there's not a lot in it, but there's enough to get it started. And then as we've got in the comments, there is Pokemon APIs we can use to Yeah. Yeah. If we wanna get into the Pokemon APIs, I'm definitely interested in that. Alright. So why don't we just pull down profiles

59:54 and then we'll grab my name. Make sure it come down. Pokemon APIs are the best hello world of GraphQL. Not to do apps? I thought it was all about the to do app. No? No. They're better. So much cooler data. Yeah. So is this correct? That looks about right to me. So that do function is gonna return either a response or an error, so you gotta assign something to it. Exactly. That's right. So then we can do the good old error check, and we can see if this thing if this thing works. That's the right that's exactly right. So then

1:00:36 if it is if it is successful, then we should be able to maybe just print response dot data or something like that. Let's see what happens. Data. Data would have yeah. There you go. Okay. That's not complaining on my end, so why don't we give it a try? Print in the error. Print the error. Oh, yeah. We should print the error. That's right on on run 21 there. Oh. Actually, you know, we can we can do an alternative to OS dot exit. We can do log dot fatal. Fatal allows you to actually pass in the error as a

1:01:18 as a as a parameter. Alright. Shall we try to run this? Let's do it. Yeah. Let's you do you a share terminal? Go tool chain? I don't know. I haven't I haven't used this share thing in a long time. Do you wanna try to pop one up? Yeah. There you go. Do you have the Go tool chain installed? Yeah. You only think of us It's running on your machine. Yeah. You should be able to run it here, and and we should be check. Show you? If you use the same terminal, we have two right now. So don't type in your

1:01:51 first one. I guess I could just click it. Oh. No. Wait. That's my terminal. Yeah. If you click in the shared terminal. Okay. I don't even see that. Let me find it here. Oh, there it is. Shared terminal. Is that Yeah. There we go. Oh. ASPS. There we go. Alright. Cool. This is awesome. I love this live share thing. Alright. So we're going to we're gonna go run this thing. So we're gonna do our cap slash tester slash main dot go, and we're gonna see if this works. Hey. Look at that. Fast. It worked. We're getting some data. Even

1:02:31 though you had me tape it. Oh, that's That just goes to show that the library is really solid. I didn't ship it. Can we do this again? Let's make a let's make an error. I wanna see what the errors look like. So if I do all profiles z z z z z, I save that. Yeah. And then I And then I'll I'll extend it again. Oh, it looks like it didn't, it didn't detect the errors, so we that that seems like a bug in our, in our end. Well, why don't we check out what's going

1:03:00 Handling GraphQL Errors in the Go Client

1:03:08 on there? I'm curious. We wanna follow-up. We may get one of several errors here. We might get a 200 response with an with the errors, array. We might get a 400 response with the errors array. We might get a 500 response with the errors array, or or we may get nothing. You know? Right. Alright. Well, we can we can account for all those. Do you wanna follow my cursor again, David? We can try to add a little bit of robustness to this thing. Following? Yeah. There we go. So what we'll do is once we've gotten this response back and

1:03:42 we've kind of read the the data out of it, we can actually go and add some extra checking here. Do you wanna deselect that text maybe? There we go. So what we can do is we can say if the response dot status code is not a 200 or we can say is greater than 299, that's what I usually like to do, then, we can return, the oh, actually, we wanna do this after we've parsed the, the JSON. Okay. So the problem we never got the error there was because the GraphQL server is actually saying 200 okay

1:04:19 and the errors and the response, which is why we never got the error handling. Got it. Exactly. And so If if errors is not null or undefined or whatever, that's when we know we have a GraphQL error. There still might be GraphQL data, but that's that's when we know something bad one happened, like, hands down across every server. So what we'll do here is we'll say else if the GQL response dot errors were in fact did we make that Omid empty? I can't remember. We did. It's empty. So we'll say we'll say if it's not nil and

1:04:54 the length of it is greater than zero, we will return the response and an error saying that the graph QL I cannot type those words in that particular order for some reason. GraphQL returned errors, something like that. So that Would you really make that an else if, or would it just be two ifs? I guess it could be both. Yeah. You're right. So Yeah. Yeah. So this way, we'll still return the response so you can actually see what's inside of those error objects, but we're also returning a non null error object so that when you're

1:05:36 in main dot go, when you check to see if the error is not null, we will actually catch this log that fails. So now, theoretically, if I did if I did that right, we should be able to run this again, and we should get an actual error. Tada. I'm only logging in. Ah. Can we Okay. So Can we always log the whole request instead of just the data? Sure. So we can do a A response. Sorry. Println response. Yes. Just so that we can see exactly what it's complaining about. Okay. Nice. So nice. Yeah. It it looks like it it

1:06:14 returned it returned that, and it's actually gave us a suggestion. Yeah. Something Francis said earlier caught my attention was that they can get partial returns. So I was curious of, like, can can we actually do, like, one valid and one not valid and then run it? With is that correct? Yeah. Oh, okay. That that would absolutely work. Yeah. K. Okay. Let's try it. Yeah. I'll run that and let's see if no, I think. Actually, it might be that query is no on this case because query had returned an error. But let's find out what your server does.

1:06:50 Yeah. Let's see what it does. We could always make surname, you know, have a z at the end. I see. So you know what happened here is because we we oh, yeah. So it does look like it worked. So we got nothing for the data, and then we got an error. So it does look like it fails on that part of it and did not give us partial data back. So I think let's try this because this should give us yeah. I think this would give us what we want. Okay. Should I run that? Yeah. Let's try it.

1:07:30 Alright. Not quite. So this is fast holding. I should also caveat that this is a GraphQL server that generates it based on JSON data with no types and stuff like that. So it's not like a fully fledged Apollo GraphQL server. Probably doesn't have all these bells and whistles. So maybe this is probably as good as it's gonna get. You know why it's not executing? Because it's not a valid query. There's a query validation step that runs first to make sure it matches the schema. Because this doesn't match the schema, it's not. If you made one of the resolvers throw

1:08:02 an error or say you have a connection and you can put in some invalid range or something that will throw an error, that's when we'll get partial data back. But this isn't this isn't even executing at all. Your server's going, you're giving me bad input. I'll shut you down before I start using any of my resources. Okay. That makes sense. That makes perfect sense. Alright. So if we reformat that to be correct, then we should be able to run this and get a good response back. There it is. Alright. Very cool. Alright. So we're gonna oh, I should probably

1:08:36 have a branch for this. That would be a nice idea. Yeah. I'm I'm gonna name it Connor. I'm gonna get name it Rawkode GraphQL because You need dash b. For Stella. Oh, yeah. I do need dash b. You're right. Nope. That's not a dash. My typing skills go to hell whenever I'm on a whenever I'm Francis and I are getting cooperates on SPR too, by the way. Just Uh-huh. So we'll say basic working GQL client with Rawkode and Reconbot. There you go. Nice. Oh, I gotta add stuff first, though. Alright. Hopefully, you won't be able to see my

1:08:38 Committing the Go Client Work

1:09:22 my password manager pop up with oh, you we're not even sharing my screen, so we're good. Nope. I agree. I'm worried about the terminal. It seems okay. Yeah. No. It's it hides it hides it there. Cool. Alright. So we have got the basic one working. So now we'll move on to the next step, and we wanna take this Go client that we've created here, and we wanna expose it as a host function that can be called by a, by a a WebAssembly module. So we're gonna quickly go back to that capabilities, struct, and we're gonna add this as one

1:09:38 Exposing the Go Client as a Host Capability

1:09:58 of the, available, capabilities. Still cannot take that. Forgot about all this. I was like, hey. We were we made the client. Yeah. So we're gonna set it for the so there's a function here for default capabilities. So we're just gonna fill that in in as well so that, you know, by default, modules can have that that GraphQL client available to them. The nice thing about Reactor is that if you wanna pass in a custom version of this struct to a particular module, you can change it per module, and that's how you define which module is able to access which capabilities.

1:10:41 So that's kind of a a neat a neat part of this. What is Why is it mad at us? It's it's mad at us. Oh, because we need to make this a pointer. I will I will come back and clean some of this up later. Cool. So now we've got this capabilities object that contains our GraphQL client, and we're gonna go ahead and expose it to the WebAssembly modules via a host function. So we're gonna pop over to the r wasm package, which contains all of our different host functions. And I'm gonna create a new file called

1:11:08 Defining the Go Host Function Interface

1:11:16 API GraphQL, and that's where we're going to meet. There we go. So our wasm is the reactor wasm package, and we're gonna need to define one of these functions. So I personally am just gonna start by copying this one because it's gonna be somewhat similar. So we're gonna have this one instead of fetch URL. What should we call it? Query just query, I guess. Yeah. GraphQL query? Yeah. GraphQL query. Yeah. Alright. GraphQL query. So in Go, you usually don't use a snake case or kebab case or whatever you call that. But in the reactor code base, I like to use the

1:12:02 underscore to define that this is a host function so that we don't accidentally get confused later down the line because I know I've done that a couple of times. Alright. So for this particular host call, we're not gonna need a method, so we're gonna get rid of that one. And but we are gonna need a URL. But I'm we in our GraphQL client, we called it an endpoint, so I'm gonna rename it to endpoint pointer and endpoint size. And then we're gonna wanna be able to pass that query. So instead of body pointer, I'm gonna call it query pointer

1:12:33 and query size. So as we talked about before, this will be the location of the query in the WebAssembly module's memory, and then this will be basically the length of that, memory so that the host can find it. Now this identifier is a string. Yes. It will be a code in a string. Yeah. Go on. Go on. You're gonna get to it. Yeah. So this identifier is something specific to Reaktor, and this is how we how we are able to run many, many, many, many WebAssembly modules all at once is each each time you execute a WebAssembly module, it's given

1:12:48 Discussion: Passing Complex Data (Variables, JSON)

1:13:08 an identifier just so that we know, you know, which module's memory to go look at when we're pulling that string out of the, out of memory. Alright. So, we're going to I guess we didn't implement it, but how would we do, variables with that call signature? Yeah. You're right. So for, the this is actually a tricky thing, and I actually don't particularly love the way I've implemented this. But in in the HTTP call, for example, we actually encode we encode headers for HTTP calls by, you know, sticking them on the end of the URL with, like, a custom encoding

1:13:51 that I really don't like the way that it currently works. So I'm we're not maybe we won't replicate that here because I don't wanna continue propagating, you know, bad practices. It's something that I've been meaning to improve, and this code is and yeah. In in the at Shopify, we've handled this with message pack. And so we we just write message pack in and out. And so you all you're doing is passing this pointer around for the string of the input and the output. There are limitations there too. And then we also we started I don't know if I could talk about

1:14:27 it, but there's a world in which we might like JSON. And because JSON could parse real fast in certain locations, and so we're like, okay. So, you know, maybe it's just JSON in and out. Yeah. But that I think there will be a future version that's based on JSON. The, the reason I didn't go with JSON in the first place was that Swift is one of the languages supported, by Reaktor, and working with JSON in Swift in WebAssembly is actually kinda broken, or at least it was when I first implemented this. So I went with a,

1:14:57 less nice but, worked version than, you know, the the ideal version. Cool. So we're going to gonna come around about eventually, you think? Hopefully. Hopefully. I'm I'm hoping that, you know, there will be some kind of wazzy proposal that comes out soon that I may or may not be working on related to some of these things, but that's all I'll say for now. Okay. Cool. So we're gonna add this little piece of boilerplate here. So this this takes the identifier that I just talked about, and it actually gives us the WebAssembly instance, that is associated with that identifier, and it

1:15:30 Reading Data from Wasm Memory on the Host

1:15:39 allows us to actually go in and read that memory. So when this host function gets called, we're gonna find the WebAssembly module associated with the identifier, and then we're gonna read the memory out of that instance. So we're gonna do instance dot read memory, and we're gonna pass in the endpoint pointer and the endpoint size, and that's gonna result in some bytes. So we're gonna say the endpoint bytes is here, and then the endpoint is the those bytes converted into a string. So we've taken the raw WebAssembly memory, and we're gonna convert it into something that we can actually use with

1:16:16 our GraphQL client. This can basically so, theoretically, yes, it can fail, but the read memory internally has some checks to ensure that nothing really bad happens. Cool. Yeah. So we're going to do essentially the same thing for the query, and this is going to be with the query pointer and the query size. So no. Query size and then the query bytes. So now we have basically the two things that our GraphQL client needs in order to actually do this properly. Oh, we are right. The, let me just import that. That's why this is complaining. Hey, Gabriel. We're using, live share.

1:17:11 Yeah. Something that I've never really used a lot before, and it's really, really cool. It basically turns Versus Code into Google Docs. It's really awesome. It's working. Yeah. So now, now that we have the two inputs that we need, if we go back to our GraphQL client, we'll remember that we need the endpoint and the query as the input to our code that we wrote earlier. So we're gonna take you know, now that we've read the endpoint out of the WebAssembly memory and we've read the query out of the WebAssembly memory, we're gonna actually make that call. So

1:17:40 Calling the Go GraphQL Client from the Host Function

1:17:43 we're gonna we're gonna grab the instance. It has what's called a a CTX or a context, and this contains that GraphQL client that we added before. So this is how capabilities are passed to the WebAssembly modules. That capabilities object gets given to the WebAssembly instance when it's executed so that we can use those capabilities in a fun way. So we're gonna call that do method on it, and we're gonna pass in our endpoint and our query. Yeah. This is where the magic starts to happen. So we're gonna get our response and our error from that, and I don't wanna call

1:18:20 it error because that would be the type, not the actual variable name. And now another part where WebAssembly is a little bit challenging and where I think the reactor code base needs some work done is that passing those error messages back to the WebAssembly module is not yet currently possible. So as kind of an interim solution, we're gonna use this this logger object that is available, to just print the error message, if if one occurs. So we can Instead of yep. No. So so instead of writing, like, into a area of memory that that they've reserved for a response,

1:18:30 Host Side Error Handling (Temporary Logging)

1:19:07 something like that? So yeah. So, eventually, that is the plan. Reactor just doesn't support that yet. Okay. But it does support passing back just the successful response. So now we've kind of gotten to the point where, oh, we're gonna, return, return a negative one if this happens. So, because of WebAssembly's limited, you know, ability to have really complex types, the thing we're returning here is an int 32. And, when an error happens in reactor, we return a negative number to indicate that it's kind of similar to bash or or a shell. When something bad happens, you return, you know,

1:19:38 Host Function Return Value (Size or Error Code)

1:19:49 something, you know, a nonzero or a negative number to indicate that something failed. But if a successful call happens, the return value is actually the size of that response. So it's a bit of a it's a bit of a tricky little interface that hopefully, you know, the WebAssembly spec will make a little bit less complicated over time. This is like old it's like my c code. Yeah. Exactly. So, you know, web and that's one of the one this is basically the reason why I built reactor was so that it could hide all of this complexity from you, you could

1:20:24 kind of just use it as a normal library and not have to worry too much about it. Alright. So we're going to continue copying some of the code from the HTTP version of this just to speed things along a little bit, and we're gonna do essentially this. So what we're gonna do is we're going to the response is currently a struct, but we're going we need to serialize that into something that can be written into WebAssembly memory. So we're gonna actually convert that to JSON and write that into the the WebAssembly module. So we're gonna use that

1:20:40 Writing the Response Back to Wasm Memory

1:21:03 the response, We'll call it response bytes to be consistent, is JSON dot Marshall. Again, we've seen this before. And then if the error is not null, we're going to, do the same thing as we did up here. Out of curiosity, since would it always make sense to to to write these bytes because the response is useful on air? Or is that is that supported return? So, yeah, that that would Reactor would need to learn how to do that because, the negative their negative response code means that we can't tell it how big that, response is.

1:21:42 Ah, I gotcha. Yes. So, we're what we're doing is we're converting it to bytes. We're we're using the set FFI result function to write those bytes into the module, and then we're returning the size of those bytes so that the module can then go and find it in memory and reconstitute it. Cool. Cool. So we have our, this is our host call, basically. We we've now defined an interface, that the WebAssembly module can use to ask the host to make a GraphQL query on its behalf. So, we do need to do one little extra step, which I'm

1:21:58 Creating the Go Host Function Wrapper

1:22:21 just gonna, again, copy and paste. We need to create a wrapper around this host function to pass to the Go object of the of the WebAssembly instance. So, this is just boilerplate code. GraphQL query, we'll call it, and we have to tell WebAssembly the the shape of this function. So, we have to tell it that, you know, the what exactly the different arguments are, so that we can URL. Yes. You are correct. So this is endpoints pointer. Gabriel, this is gonna run-in Suborbital, which is a server side framework for executing Wasm. So it's not gonna run-in a browser.

1:23:02 It's gonna run it's running as sort of the stateless runnables in the Suborbital project. Is it suborbital.dev? Is that the It is. Yeah. Suborbital.dev is the website. Yeah. So this is the reactor project, which is the inside of Suborbital, that is the core WebAssembly execution engine that that Suborbital is built on. Okay. So we are just creating a tiny little wrapper here that should just be two more seconds, and then we will do the query pointer and the query size. So this this wrapper, all it's really doing yeah. Go ahead. Can I teach you a

1:23:11 Q&A: Wasm Use Cases & Garbage Collection

1:23:44 trick? Yeah. By all means. Yeah. You can hit if you click on a variable and you hit f two, you get a little rename, and you can it'll go and rename everywhere. There you go. That is the fancy stuff that I come on these streams to learn about. Cool. Yeah. So this this, this wrapper is essentially just, taking this host code that we've defined and telling the WebAssembly instance about it and giving it information. So, now that we have these, and we've passed I just wanna double check to make sure we've passed the right things. Is that the real capabilities?

1:24:25 Right? Because, like like, the other capabilities, that's in Go. But it but, like, it's not but this one here is the one that actually gives it to the to the the host module, right, or the guest module. Yes. So yeah. So this is given to the guest module. This calls our GraphQL query function, and then the GraphQL query function grabs our capability to actually perform the the action. So there's a there's a bit of a rigmarole that you have to go through, but that, you know, is what's needed to make it as flexible as it is.

1:24:55 Cool. So the last thing we're gonna do is we're just going to find the random point in the code where this is all added to the module. So we're gonna take that that GraphQL query wrapper function that we just wrote, and we're gonna include it in the host functions that are given to the module. And we should be pretty much ready to to switch over to to WebAssembly. So do we have any any, maybe, comments or questions we wanna address before we move on? So Gabriel did ask if this is gonna run on a browser, but there was a kind

1:25:30 of a second part of that, which is why would we use Wasm on the host? If you got a Yeah. That's that's a great question. So, one of the great things about WebAssembly and and the one of the reasons why I chose WebAssembly as the technology to power Suborbital is that you can have multiple different languages coexisting inside of the same application. So the, you know, people who use Suborbital, they often have different teams within their company that are comfortable with different languages. So maybe they'll have, you know, a mobile team that is used to writing Swift, or they'll

1:26:05 have, you know, a a web team that's used to writing TypeScript. And so they can actually write code in those different languages, and they can all coexist inside the same server architecture without you needing to do anything crazy to to make them communicate with each other. Okay. We also have a question from YMO, but I also point out that Noel has pointed a bug in our code where we are returning fetch URL instead of GraphQL query. Where is that? Oh, I see. There we go. Okay. So why am I always asking about garbage collection? Specifically,

1:26:45 go and wasm looking at lane 17 to 20, which I think are now 34 onwards. I'm curious about garbage collecting. Yes. Yeah. That's right. So when we flip over to the Suborbital Rust library, you'll see some of this. But so WebAssembly itself does not yet have a built in garbage collecting paradigm. That's, again, one of this one of the proposals that is currently being put through the standards body is a garbage collecting proposal. But the you know, currently, the the suborbital hosts and guest code collaborate together to kind of do garbage collection. I don't wanna use the word manually, but

1:27:27 it is kind of manual where, know, the reactor is telling, the guest module to clean up memory at the appropriate time, and then, like, the guests the guest code complies and actually does it. Okay. I just wanna check with you both. Like, we're at the hour now where we said we would finish. Do you wanna go on for another ten to fifteen minutes and get this working, and is that okay for both of you? Yeah. I'm cool. Let's go for it. Yeah. I'm excited. Alright. Awesome. We'll try to power through the end here. So yeah. So we're gonna switch over to Rust

1:27:58 Switching to the Wasm Module Side (Rust)

1:28:00 now, and we're going to I'll pretend like I understand everything from here on out. Hey. I think of the three of us, David is the Rust guru. So Oh, no. I'm totally Alright. So, you know, once again, we're gonna we're gonna do some copying and pasting just to make this go quickly. So we're gonna define a a GraphQL. Again, cannot type that word. We're gonna define the GraphQL the GraphQL module within the Suborbital library, and we're going to define that that host function as something available to it. So we're basically doing the mirrored, the mirror to the last,

1:28:15 Defining the Host Function Import in Rust

1:28:47 the host call where we're gonna just expose it here inside of the inside of the module. So I think that should be good. We've got our endpoint pointer, endpoint size, query pointer, query size ident. Perfect. Alright. So now we need to create a a an actual, you know, usable function that somebody can go ahead and call. So we're gonna define a pub f n f n, and we're gonna just call it query to keep things simple. And we're going to take in an endpoint, which is an STR, str string. I don't know how people pronounce

1:28:50 Creating the User-Facing Rust Function

1:29:28 that in real life. And we're gonna take in a query, which is also an STR, and we are going to return probably a result, I would assume, of bytes of VecU eight comma run error, which is a a suborbital defined type. So Why wouldn't they wanna JSON parse here? So we will to keep things simple, I think the caller is going to JSON parse because my Rust skills of being able to pass in a struct, parse it, and then all that stuff, I don't think I'm able to do that in the next fifteen minutes, so we're

1:30:12 just gonna keep it simple. Alright. So oh, we need to I don't know why it's complain oh, right. We need to actually use the full path for this. There we go. I think that should be good. Yes. Alright. So it's just complaining that we're not returning at this point. So we should be able to do something fairly similar to this where we so we wanna take the the endpoint and the query, and we wanna convert that into memory that can be transmitted to the host. So we're gonna try to do this in a fairly quick fashion. So there's a couple

1:30:15 Preparing Data for the Host Call in Rust

1:30:54 of helper functions inside of this library that will make it a little bit easier for us, and I just need to find them. Right. So we're gonna be doing as slice and as pointer, I believe. Yeah. Let's just do that. So we're going to say that our our endpoint slice is the endpoint as a slice. Of course, that's going to complain. This is where David might need to help me here. Oh, right. We need to It's already a slice, isn't it? You'd think. The body is oh, here's vector. It's a vector of bytes. Yeah. So you can So we need to

1:31:54 Sorry. Go. We just need to do how do you convert a str to a vec a asvec, I think? Is that no. We don't we don't need that. Right? Oh. Oh. You you go ahead and take over. Why? Oh, there we go. And so we can get as base, which is gonna be yeah. You don't want yeah. You don't actually want a vector. Right? You want the so just as base We do want a vector. We do want a vector. You do you do want a vector? Yeah. Because the vectors will write into WebAssembly module.

1:32:25 Calling the Go Host Function from Rust

1:32:35 Yeah. Slice would work. Okay. And what are we doing with the pointer? So that pointer right. Exactly. And then we wanna take query. That would be the endpoint pointer. Oh, yeah. Oops. We're gonna do this twice. Once for the endpoint, once for the query. There we go. Alright. Okay. This is why David this is why I wanted to do this on the stream. No. I can't type. There we go. There we go. Alright. You got it. So what's the we are yeah. No. So now we're gonna actually make that host call. So now we're going to say that the results

1:33:31 we're gonna let results size equal the so we're we have to do we have to use the unsafe directive here because we're actually calling something that is being given to us at runtime, which is, you know, in Rust terms, something unsafe. So we're gonna say we're gonna call that GraphQL query function. We are going to pass the endpoint pointer, and then we're going to do this would be endpoint slice dot len, I think. Slice dot len. Is that yeah. That's valid. And then query pointer and then query slice dot len. For some reason, I keep typing zed instead

1:34:18 of c. And then we're gonna pass in that ident as the last parameter, and that should cannot find GraphQL query in this scope. Why not? Did I misspell it? It looks like it it does say there's a different it's hard to do when I'm Sorry, honey. I'm I'm scrolling around. It's my fault. Okay. You just kinda spell. Oh, well, that that would explain a lot. Alright. Cool. So alright. We need to convert these to as I 32 and as I 32, and this cannot find ident in scope. Oh, that's right. We need to do super

1:35:13 it's a super call. Super state ident. That's what it is. Apologies for the copy and paste. I wanted to do a lot more of this live, but we are running out of time. Alright. So we're gonna think I know this. Semicolon will go at the end. Okay. There we go. So now we've called the host function, passing it the pointer and length for each of our two parameters, and then we're going to take that information, and we're gonna actually do something fun with it. So we're gonna use this this helper function that's included in this library

1:35:42 Reading the Response from Wasm Memory in Rust

1:35:48 to accomplish that in a way that's not super gross. So we're going to basically, read the memory from the the, you know, that the host has given back to us and passing that size as the parameter, and this is going to give us either the the bytes or an error. And so because the return type of this function is either bytes or error, this kinda just satisfies it. So I believe at this point, we have everything that we need in the in the library. So should we try to actually use this thing? Definitely. Alright. Hopefully, we can do this in in

1:36:28 a quick five minutes and be done for the day. Alright. Alright. So we're gonna create in we're gonna go inside of this in this test data function package because that's where I keep all of my all of my testing stuff. So we're going to use the the Suborbital CLI to create a runnable called, let's say, r s ref q l. Still cannot take that word, and we're gonna put it inside of our wasm our wasm slash test data. I think that should do it. Alright. So now we have a new this is a reactor runnable

1:36:30 Setting up the Rust Wasm Test Module

1:37:15 that is written in the Rust language, and we should now be able to take advantage of that GraphQL function we just wrote within here. So we need to do a quick little trick just to make sure it's importing the right version of the library because we wanna use the local version instead of a version from the cargo package manager. So we're just gonna edit cargo dot toml real quick to trick it into using the local version, and then we're gonna get to work. So, hopefully, if it accepts all of this, we should be able to use it.

1:37:54 Hopefully, cargo dot toml. Hopefully. Sometimes I need to restart my my Rust server in order for it to like that after I do that import. External location does not exist. It seems to like it, but for some reason, it doesn't. Well, we're gonna just try it anyway. So, we are going to, call. We're gonna import the Suborbital GraphQL. It knows it's there, so it's obviously just complaining for no reason. Let's put something on there. I don't know why it doesn't like that one, but it likes this one. Anyhoo, so we're gonna call, query, which is from, like, GraphQL,

1:38:20 Implementing the Rust Module Logic

1:38:46 one there. Do you wanna fill in the endpoint and the query there for me, David, like you did in the other example, or should I just copy it? That's alright. I think that's right. Shouldn't I probably have an HTTPS on the front of that, I think. Fussy pants. And do we need a response? Yeah. So we need to put that in something. So that is going to be a result type. Oh. And so we're probably gonna wanna match on it. Yeah. Time for a speed round. Don't know where this goes yet. So It's a response. Right?

1:39:48 What what alright. Okay. Yeah. Response. Blah. Yeah. Definitely print it. Do we have a logger? We do have a logger. Yes. We do. Does print l n work here? I have no idea. Okay. Oh, it's okay. I'll I'll fill it in after that. There you go. So we will we will we'll use a suborbital log for this, which will give us some actual we're going to, log in as info, I think, not into info. No. It'd just be info. Yeah. Lowercase. So we'll just log that help me. I guess we actually wanna log the the

1:40:34 string version of that response, don't we? So we'll want to do response oh, we want string from is that right? Mhmm. Yep. Well, it depends what response is. It's just page. Right? So it should be okay. S j r. Yeah. I think that should work. It's it's not yelling at me, so that's that's that's decent. And then we will log if we get an error here, we will log that ee dot e should have a message, I think, if I remember correctly. Dot message spelled properly, I believe. And an and an error type. Right? Or is this your message? Sorry.

1:41:23 This should be this will be a generic error message because, yeah, there's not much we can do there. Alright. So it it's only yelling at me up here, but I feel like that's a fake error because There's a restart complaining about anything else. If you pull up your command, you can restart Yeah. Okay. What's it called? Definitely had to do Language server? R l s. Yeah. Yeah. Oh, it could be that the that it's that the that the library has an error because it seems like it does. Why doesn't it like us here? Do I

1:41:38 Debugging and Compilation Issues

1:42:00 have to return here? No? No. Because there's no semicolon, so we don't have to return. Although, I was telling this that we're not returning. Oh, no. But Right. It's complaining about a bunch of different stuff here. Well, it's complaining that we got the user size I 32. We're so close. We are so close. Yeah. Let me let me compare it to the other one because that's how I usually that's how I usually write code. It's just compared to something that I've done in the past that worked. Let's see. It doesn't seem to complain here about that. We're doing the same thing here.

1:42:47 So I wonder why this one would be complaining about it. Does that have a comma? Yeah. There's supposed to be a comma there. You restart the server the language server? Yeah. Yeah. I just did that. Doesn't seem to have Let's let's try and just let's pretend that it returns that. And then it's complaining about this. Oh, yeah. If we can just ignore that now. Hopefully. Okay. So now he's only complaining about our use size and I 32. Yeah. Just trying anything. We do have the I 32. Yeah. Yeah. I'm not sure why it wouldn't like

1:43:59 that. It says you can only convert it by doing trying to an unwrapping because that might view size might be larger than an I 32, so it might panic. That's strange. What do I do down here? Yeah. Dot line as I 32. That's what we do down here. Did I get that right? Nothing that found on you said. Okay. Thanks. Gotta love it when you're right at the finish line and it doesn't like it. Yeah. Well, do we define the inputs of GraphQL query different than we do for the other thing? So, yeah, our endpoint pointer is a constituent,

1:44:58 and our size is nine thirty two for each of them. So endpoint pointer is a constituent, which is why it doesn't mind that. But then for some reason Okay. Let's try you said it was potentially a VEX, so I'm gonna force it. Mhmm. And what was it a VEXR? Right? Yeah. Enter. Does that help? Oh, sorry. I'm scrolling around. Yeah. Let's see. When we take this URL and we convert it, what do we do here? We do URL string as str dot pointer and then URL string dot link. Why don't we try just copying this? Oh, you're you're using uppercase

1:45:59 strings. Right? Nope. These are ampersand str. And no. But then the bottom, you have this follow thing is I'm gonna turn you off now. Yeah. That's still that's okay. So we'll take Yeah. You're using format here. Right? And string from. So these are uppercase strings that you're using length on and slice. I see. Okay. Could always do a string from endpoint and then as slice. Why is it complaining about that? As slice is not a thing. Is that not what we just done here? Might be. It might be as back. URL string. Oh, no. You're just you're you're just using

1:46:57 a string. Right? Okay. Sorry. Yeah. And then that has a length as an I 32. This is well, that should be endpoint slash. Right? Why do computers hate us? Very, very good question. So we are trying to pass a pointer and a length of the endpoint here. Right? That's that's our mission. Okay. So we've got the as pointer, which is fine. So I'm not really sure if we actually need these. Yeah. Maybe not. If we just do endpoint dot len. Yeah. You probably still need an access to the And I thought it's a new type.

1:47:54 But But it's not it's it's it's it's still complaining about the new size even though we casted it. Okay. So endpoint size equals endpoint dot l n. I mean, does that work? Let's find out. Endpoint size. Yeah. For some reason, it likes that, but not the other way around. Alright. So let's do query dot as pointer here, and then we can can you add the same thing for query there? Alright. Oh. What's it complaining about? Semicolon. Expect expected that. Something with my with my I think it's your language server thing. Right? Yeah. I've restarted it about four times already.

1:49:03 Can we just compile it? Unfortunately. Yeah. I think we might be able to. Yeah. That's that's that's basically what I'm coming to at this point. So if we try to build we wanna build our Wasm slash test data, right, slash what is this called? R s GraphQL r s GraphQL, and then we use the native tool chain. Let's see if it's complaints. Why? Oh. No such failed test data GraphQL. R wasm test data r s GraphQL. Did I misspell that? K. Why don't we here. We we can just build them all. Easier than trying to do it with an

1:50:07 individual one. So this is this is just Subor is gonna go through and build every single piece of test data, which should only take ten seconds or so. Yeah. You still can't spell GraphQL. That's why. Probably. Yeah. GraphQL and the path. My alright. Well, that makes sense. That is gonna be my legacy from this stream. It's just my inability to spell the words GraphQL. Alright. Should be just about done here. I can't wait. I'm just waiting for it. Oh, yeah. There it is. So now it's complaining. Three errors. Oh, we I know what we did. Oh,

1:50:57 yeah. We're just tostring instead of asstring. And you've called an error function in libris. I'm gonna follow you again. Oh, I see. I see. Okay. Right. We can't use that. We are going to instead. Oh, we are yeah. That's right. We're going to do we want the error function from the logging package instead of the error macro from the yeah. That makes sense. Okay. So now that I know that I've misspelled that, let's try to rebuild, just that one in particular. So if we build, our wasm slash test data slash r s dash g r a

1:51:58 q h q l because, apparently, that is that is how I've done it. Alright. So it's complaining. Oh, I just passed. It's just complaining about our inputs there. So if we instead of toString, it should be has str, apparently. And then No. I think it's complaining about we've got because response is back, so we're converting it to a capital string, and then we are converting that to a str. So I think I think that's what it wants. Okay. Let's try. Yeah. We'll see. No. Oh, that's right. We can't, I think there's a I have a util

1:52:55 that will help us here. We can do util. It's because we don't know what encoding the vec is. Right. We want to string, and that will be response. And then we'll do a str from there. And then message is apparently something unknown. E dot message. That should be the one error. Yeah. That's that's that one should be okay. What type is e? Because I'm not I'm not getting any e should be a run error. Okay. Yeah. Assuming we've done our yeah. Run error. And run error's message is an uppercase s string. So Right. So that's why, yeah, that should do

1:53:49 it. Okay. Try that. Hey. There we go. It built. Alright. Hey. So now Run it. We should be able to we should be able to run this module and actually get something usable. So now we're gonna switch over to our the fastest way to get this running is to just use a Go test. So we will let's find one that's doing this properly. We will copy this one here, and we'll create a brand new test called oh, I barely didn't like that. What did I copy around here? I wanna copy this whole thing, and we're gonna create a new test called

1:53:51 Successful Wasm Module Compilation

1:54:30 tests, let's say, GraphQL QL runner with alright. So we're going to import the r s dash g r a. Make sure I make sure I spell my typo correctly. G r a q h q l slash q h q l dot Wasm. Alright. So now we're going to pass in well, it's already running the query the way that we want. Right? So we don't need to pass anything into it. We can just pass in, an empty string there. And then assuming everything goes correctly, we should be able to say print l n, the response, which is an interface type, and we wanna

1:55:30 convert that to bytes. Would this be the logs? This would be what it returns. Oh, you know what? You're right. It's logging, so we don't really need to actually do anything here. Okay. Because I don't think we returned. Yeah. We just returned hello Hello. In in the module there. So we can just do nothing here and and see what happens. We're not even gonna use the response. Okay. Moment of truth. We're gonna run this. Cross your fingers. See what happens. Will you be able to see that output? No. Oh, I don't think you can you

1:55:58 Running the Rust Wasm Module

1:56:08 you can't see my output. Okay. I'll I'll run the I'll run the test command manually, and then you should be able to see it. So if I switch over to that terminal, you can see that. Right? Yeah. Okay. Yeah. Yeah. Cool. So let me just format this correctly. We're gonna make it verbose, and we're gonna run the local version. And There it is. Our data. Hi. I recognize that name. Alright. We did. That was a very long walk, but we got there in the end. Awesome. Oh, that's fantastic. Yeah. So we're using, yeah, we're using reactor

1:56:26 Demonstration of Successful Execution

1:56:49 to load this WebAssembly module that we built, and then we are calling it, and that WebAssembly module is making a query to David's GraphQL endpoint. That is passing the query to the host. The host is doing the request, passing the response back to the module, and then that is getting logged by the module. So that was a very long walk, but we got there in the end. That's a beautiful thing. Yeah. That So so that was super file. We can, like, delete the Rust, keep that RUSM file, email it to a friend, and they can run it. And it would go

1:57:14 Summary and Concluding Thoughts

1:57:26 and hit API dot Rawkode.dev. And as long it has the right capability functions, then it would be able to log, do the GraphQL, and give us give and give us that output. That's alright. Exactly right. So, you know, when you at the top of the Rust file, you saw we were importing the different modules from the Suborbital library, and that was basically importing those capabilities that we had written earlier in the stream. So it all comes together, and and we were we were able to to conquer the dragon. It's a beautiful thing. Oh my goodness.

1:58:00 Alright. Well, that was very, very cool. I really enjoyed that. I learned an absolute ton. So I'm gonna say thank you, Connor. Thank you, Francis. It was a pleasure to sit and work through that with you both. Any last words? Yeah. I don't think so. It's a lot of fun. I'm gonna drop a link for Gabriel into the Discord chat about some other capabilities we're doing with Wasm or people out there are doing with Wasm where, like, because of the nature of it, you can, like, boot a program and then pause it and then and then use it

1:58:34 to execute, you know, input from there. There there's, like, all these it because it's because of that isolation, there's this entire world of things you can do for very fast computing. And and, you know, and I'm just, like, like, waiting for a game to install the other day. And I'm like, if this was WASM, we could, like, stream it, you know? Like like like like, I could just start playing it because it just start reading the memory from the beginning. Right? You know? Yeah. We're not in that world yet. And yeah. And and the portability of it is

1:59:01 awesome. Like, if if a browser were to expose that same GraphQL query host function, you could take this exact same Wasm module, run it in Reactor, and then also run it in the browser. And that's the portability that you don't get with any other technology. It's really quite cool. Yeah. Alright. We will leave it there, but we will fill up the description with more links to more awesome stuff on Suborbital and others. Thank you both again. Yeah. Have a wonderful evening, and hopefully, I'll see you both again soon. Thanks. Yeah. Cheers. Thanks everyone. Bye. Have a good day.

Technologies featured

Meet the Cast

Weekly Cloud Native insights

Stay ahead in cloud native

Tutorials, deep dives, and curated events. No fluff.

Comments, transcript, and resources

More from Rawkode Live

View all 173 episodes
Rust

More about Rust

View all 22 videos

More about Suborbital

View technology

More about WebAssembly & WASI

View all 17 videos