REST API with Rust + Warp 5: Beyond test utilities

rogertorres

Roger Torres (he/him/ele)

Posted on April 14, 2021

REST API with Rust + Warp 5: Beyond test utilities

Last part, folks!


Time to say goodbye. Warp 5.

The code for this part is available here.

Warp's test utilities are great, but I didn't want to end this series without actually being able to serve what has been built and to curl against it.

To do so, I first created a /src/bin/main.rs file. Then, I changed Cargo.toml to add this:

[[bin]]
name = "holodeck"
path = "src/bin/main.rs"

[lib]
name = "holodeck"
path = "src/lib.rs"
Enter fullscreen mode Exit fullscreen mode

As I mentioned at the beginning, it is easier to find examples of code serving and consuming the API than testing it using the test utilities; so I'm gonna fly over it.

My main.rs ended up like this:

// I made "models" and "filters" public in lib.rs
use holodeck::{ models, filters };
use warp::Filter;

#[tokio::main]
async fn main() {

    let db = models::new_db();
    let routes = filters::list_sims(db.clone())
        .or(filters::post_sim(db.clone()))
        .or(filters::update_sim(db.clone()))
        .or(filters::delete_sim(db));

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
Enter fullscreen mode Exit fullscreen mode

And that's all! I just had to put the filters together using or and serve. The clones work because we're cloning an Arc, which doesn't create a new copy, just a new reference to our Mutex.

Below you will find some some curls I made.

POST

curl --location --request POST 'localhost:3030/holodeck' \
--header 'Content-Type: application/json' \
--header 'Content-Type: text/plain' \
--data-raw '{
    "id": 1,
    "name": "The Big Goodbye"
}'

curl --location --request POST 'localhost:3030/holodeck' \
--header 'Content-Type: application/json' \
--header 'Content-Type: text/plain' \
--data-raw '{
    "id": 2,
    "name": "Bride Of Chaotica!"
}'
Enter fullscreen mode Exit fullscreen mode

PUT

curl --location --request PUT 'localhost:3030/holodeck/3' \
--header 'Content-Type: application/json' \
--header 'Content-Type: text/plain' \
--data-raw '{
    "name": "A Fistful Of Datas"
}'

curl --location --request PUT 'localhost:3030/holodeck/3' \
--header 'Content-Type: application/json' \
--header 'Content-Type: text/plain' \
--data-raw '{
    "name": "A Fistful Of La Forges"
}'
Enter fullscreen mode Exit fullscreen mode

GET

curl --location --request GET 'localhost:3030/holodeck'

curl --location --request GET 'localhost:3030/holodeck/2'
Enter fullscreen mode Exit fullscreen mode

DELETE

curl --location --request DELETE 'localhost:3030/holodeck/1' 
Enter fullscreen mode Exit fullscreen mode

The good thing about using warp's test utilities is that when the time comes to serve what you built, there's no extra effort; no refactoring. So if you are using the tests, I strongly advise you to try some curls as well. Here are some problems that I just found when doing it:

  • At some point, I deleted the warp::get() from my list_sims() and as a result answering every request I made;
  • The serialization/deserialization in PUT wasn't working properly. As this didn't appear in the tests, I assumed serde was being so good at its job the ended up hiding a mistake I made in the code.
  • Delete reply had the right Status, but the wrong written message.

The End

What a journey! I really hope it ends up being useful to someone else.

Before letting you go, I would like to write a small mea culpa, to warn you about the things that are not ok regarding what was built here.

My main goal was to show the process, not the result; I didn't want to post a finished code and write a text explaining it. Although I am happy with this choice, it has some drawbacks. To make things clearer, I avoided creating and using too many functions that would compel the reader to check the previous code to make sense of it. So be aware that these things are not ok:

  • Code repetition is problem number one. Tests are creating the same Simulations, there are repeated use keywords everywhere and so on;
  • I forced the HashMap, so it never felt "natural". I don't regret it because it gave me a chance to show you the trait implementation, but oftentimes it was a bit of a hassle;
  • Warp provides building blocks, which means that there is more than one way of doing the same things, and naturally, some are better than others. I didn't stress this much, often going with the easiest way;
  • The tests are far away from actually checking everything that should be checked.

And I think that's enough. Really! Thank you for spending the time.

Live long and prosper!

šŸ––

šŸ’– šŸ’Ŗ šŸ™… šŸš©
rogertorres
Roger Torres (he/him/ele)

Posted on April 14, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related