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
Json < Bottle >
failed: Parse("{}", Error("missing field
id
", line: 1, column: 2)).
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
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!
Previously I spent a great deal of time creating a basic API for storing and retrieving bottles in an alcohol collection, as well as locations associated with those bottles and their category. Before I ever started the Rust and Rocket series, I had actually started manually recording our liquor collection