Web App and HTTP
The MVP of the web app stores paekli for delivery in a local signal. This means the web app cannot send or receive paekli to and from other components. Also, the paekli that were sent but not received are lost when the web app reloads. (That could be fixed though without any integrations, browsers do allow websites to persist data.)
These limitations will be fixed by the integration with the HTTP server.
Sharing request types
Our HTTP server expects requests from us to be in specific JSON formats. Those formats are derived from our Rust types, so we didn't need to specify them explicitly.
Therefore, the easiest way to ensure we are always sending and receiving correctly-formatted JSON is to use the type system!
- Create a shared library if you haven't already.
- Copy the request types (
SendRequest
,ReceiveResponse
etc.) frompaekli-http
topaekli-core
. You're free to organize it into a module or not. All these types need to deriveSerialize
andDeserialize
. - You might have to add some of the dependencies of
paekli-http
topaekli-core
. Don't forget to add all the--features
you need. - Run
cargo add --path ../paekli-core
for the http and web app component to access the shared type.
Sending paekli
You probably have some signal to store sent paekli at this point. We'll get rid of that signal and tell our HTTP server to store it instead. We do that with the HTTP request which the server is programmed to accept.
We already had to add the dependency gloo
, which is also going to help us send HTTP requests.
Let's start by defining the request:
#![allow(unused)] fn main() { use gloo::net::http::Request; let request = Request::post("https://paekli.buenzli.dev/paekli") .json(&SendRequest { content: todo!(), recipient: None, express: false, }) .unwrap(); }
We use Request::post
to say we want to send an HTTP request with the method POST
.
We also pass the URL we want to send it to as the argument.
Notice the path /paekli
at the end, it has to match the router configuration of the server.
You can replace the domain with localhost:3000
or whatever port you have, if you want to talk to your own server.
You can even make the domain configurable in the UI, if you like!
But I leave that up to you, hard coding is perfectly fine for our purposes.
Notice the argument to the .json()
method.
It's a reference to our SendRequest
type!
This is possible, because it implements the Serialize
trait, which we were able to derive.
We can be pretty confident the body of that request is exactly as the HTTP server expects it.
You just need to add the correct content
.
We still have to actually send the request, here's how that goes:
#![allow(unused)] fn main() { spawn_local(async { request.send().await.unwrap(); }); }
What?
What?
There is quite a bit of unintuitive boilerplate here. The reason is that browsers don't (yet) allow WebAssembly to make HTTP requests. Rather, wasm has to ask JavaScript to do it instead. Luckily, this glue code is generated by our libraries.
The browser only allows JavaScript to make HTTP requests via a browser API, which is asynchronous for performance reasons. So these weird-looking three lines of code construct an asynchronous JavaScript task in WebAssembly and instruct JavaScript to process it via its own runtime.
Still confused? Yeah, me too buddy.
You can test whether or not your send request worked by making a receive request via curl
.
If you're using paekli.buenzli.dev, you can also trigger a receive request in the API docs.
Receive paekli
The general idea here is the same as for sending paekli.
Firstly, you need to consider the correct Request
to construct.
Method, path, body... make sure to get them right.
Secondly, when you send the request in the async
block, you need to actually read the response from the server.
There is a method .json()
on gloo
's Response
type for this purpose.
It is generic, so the compiler might need some kind of hint that you want a ReceiveResponse
as we've defined it.
Also note that it's not trivial to get the response out of the async
block.
You can display an alert
from the async
block, which I recommend.
Otherwise, if you'd like a more sophisticated user experience, you can use a signal to store the received paekli.
Additional features
If you have already implemented the additional features in your web app, make sure they work with this integration as well!