Rocket and Rust Part IV (and a kegerator)
Here we are again, folks! Last time I started building my actual project: Ethel. Ethel will track information about my alcohol collection, hopefully so I can find things easier. I setup my initial implementation of my data structures (subject to change) and have a "random bottle" route which currently returns dummy data. My goal for this post is to implement my other required routes (still using dummy data, for now).
Create
So of course we need a way to create bottles. At the end of my last post, my Bottle
struct looked like so:
For our create endpoint to work, we will need to provide all of the above data to the endpoint. Since we are not actually storing any data yet, I plan on just returning the provided information back as the success response. So let's get cracking.
Because we are using serde
in our project for serialization of our data structures setting a up a create endpoint to accept json is trivial. I just have to use the data =
in my macro for the route!
Oh, and of course don't forget to mount the route to our "bottle" routes in the rocket launch:
Time to run our app and test our new route. As an aside, I decided to try a new API REST client. In the past I have used Postman, but in consultation with my rubber ducky, I was recommended to try Insomnia. While I certainly could have use curl
from the command line, I really like having collection management for an API project like Ethel.
If we send an empty post to our endpoint POST ~/bottles
we will get a 400. In the console we see Data guard
Json < Bottle >
failed: Parse("", Error("EOF while parsing a value", line: 1, column: 0)).
Makes sense, our application is expecting JSON and we did not add a catcher to our rocket application (meaning it falls back to the Rocket default). If we go ahead and send an empty JSON body, we get a different more interesting error: a 422 Unprocessable Entity. In console we see a slightly different error this time, but still a Data guard. Data guard
In this case we did have a JSON as expected, but we are missing required data to match the data structure we created earlier for our Json < Bottle >
failed: Parse("{}", Error("missing field id
", line: 1, column: 2)).Bottle
. Well, let's just copy our JSON output from our random bottle endpoint into the body of our request and try again.
Tada! We get out what we put in for now, as expected. Our basic create is done, let's move on to a list endpoint to get all of the bottles!
List (Get All)
While I will probably want something "smarter" at some point for list (such as the ability to take in parameters for filtering or pagination for large requests) we are going to start simple and get the endpoint working for now. Our list endpoint will be just like our random endpoint, but return multiple bottles! The list endpoints will also be the default route for GET
requests to /bottles
.
Since we already know how to mount the route from create and random, I will not add the code here (just remember we did it). Now let's hit our new endpoint:
Great! I now have a working GET
endpoint to list our bottles which returns dummy data. What's next?
Delete
Okay, since we are not doing "real" data yet, delete is kinda boring. Rocket supports the DELETE
http method, so it's simply a matter of using the macro and mounting the route:
Something to note: I chose to use a 204 No Content for my DELETE
endpoint. While this is my personal preference, it is perfectly valid to do a 200 (or 202 if not immediately deleting) and provide the id or object back in the response body. Do note, however, I receive a compiler warning from Rust because I am not actually using the id parameter I declared yet. Once we are actually using real data, though, the warning will go away.
And thus our delete endpoint has been implemented!
Edit/Update
Originally I had planned on only implementing create, list, random, and delete. Then I actually started thinking about using my new project and... I need edit/update. I need to be able to move bottles around and do not want to resort to deleting and re-creating them. So! Let's quickly add an update endpoint using the http PUT
method:
The code above should look very familiar. It's our create endpoint with a different verb (PUT
instead of POST
) and it takes an id parameter from our request url (id). Again, like our delete endpoint, I am getting a compiler warning about id not being using (I love you Rust).
Note: I could have used PATCH
instead of PUT
if I wanted to, which would allow me to only send the updated fields rather than the entire entity. For this project, knowing I planned on implementing a UI at some point, I decided sending the entire entity was easier.
Now we have the basic outline of all the endpoints needed for the project. All of the code was minimal, due to using the macros provided by Rocket and using the serde date structures. Here is our entire main.rs
so far:
Only ~200 lines of code! Wild. Next time, in Part V, I will actually get a database running and attached to our project so we can have some real fun!
Crisply Tart Cider on Tap
I don't belive I have mentioned my kegerator here before... When a friend of mine moved away from the PNW off to the land of kiwis, he really wanted to find a home for his magnificent kegerator. It started life as a fridge, but had been decorated by local graffiti artists as a birthday gift. Naturally, I took it off his hands (and immediately installed a second tap, one for beer, one for cider or sours).
Specifically I bring up the kegerator because over the recent Fourth of July holiday, my folks were in town. During their visit, I took them to The Woods which is the tasting room for Two Beers and Seattle Cider Co. We did a tasting and I let them pick the keg to take home for the BBQ we were having. They settled on Seattle Cider's Honeycrisp, so we packed up a sixlet and took it home to install in the kegerator. We also happened to have a Seattle Cider tap handle from a previous visit, so the kegerator was on brand!
Now I sit here, weeks later, continuing to enjoy the delectable crispness of this cider straight from the tap at home. Yes, I am spoiled, why do you ask? The cider comes in at 5.5% ABV and I find it to be a supremely balanced cider: not to sweet (blek) and nicely tart which makes it very refreshing in the crazy heat we have been experiencing in the PNW the last few weeks! On that note, I just put together two adirondacks myself and plan on enjoying the rest of my cider outside (but definitely in the shade). Cheers!