Skip to content

Commit 3d0b4fc

Browse files
celeroncoderForestry.io
authored andcommitted
Update from Forestry.io
Khushal Bhardwaj updated content/posts/rest-with-rust.md
1 parent 8f2dcfd commit 3d0b4fc

File tree

1 file changed

+124
-1
lines changed

1 file changed

+124
-1
lines changed

content/posts/rest-with-rust.md

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,134 @@
22
ShowToc = false
33
date = 2022-09-04T18:30:00Z
44
description = "Calling Rest APIs with Rust"
5-
draft = true
65
tags = ["rest_api", "guide", "rust"]
76
title = "REST with Rust"
87
[cover]
98
alt = "Rest with Rust Cover"
109
image = "/blog/uploads/rest-with-rust.webp"
1110

1211
+++
12+
Calling REST APIs with rust may seem like a daunting task at first because of the steep and long learning curve of rust as a programming language.
13+
14+
We know that contacting with REST APIs is something that we come across creating almost any other app that comes to mind.
15+
16+
We’ll make use of the `reqwest` library to make our request that is essentially a higher level implementation of the default HTTP client.
17+
18+
# Cargo.toml
19+
reqwest = { version = "0.11", features = ["json"] }
20+
tokio = { version = "1", features = ["full"] }
21+
dotenv = "0.15.0" # optional
22+
serde = {version = "1.0.144", features = ["derive"]}
23+
24+
We import the following libraries to make this work
25+
26+
| Library | Purpose |
27+
| --- | --- |
28+
| reqwest | to make our requests |
29+
| tokio | to make async requests and other async stuff. |
30+
| serde | to deserialize the json response into a rust struct |
31+
32+
### **Making Basic GET Requests**
33+
34+
#[tokio::main]
35+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
36+
let resp = reqwest::get("<url>").await?;
37+
let resp_json = resp.json::<HashMap<String, String>>().await?;
38+
39+
println!("{:#?}", resp_json);
40+
41+
Ok(());
42+
}
43+
44+
Here we do the following key things:
45+
46+
* We add the `tokio::main` attribute to our main function to use await within our function.
47+
* We change the return type of the main function from unit type - `()` to a `Result<(), Box<dyn std::error::Error>>` to catch errors from the request if any.
48+
* Then we make the request using the `get` function and await on that, and also we use the `turbofish` operator to only get the return type of the `future`
49+
* More info on why we are using this operator, [here](https://rust-lang.github.io/async-book/07_workarounds/02_err_in_async_blocks.html).
50+
* Then we deserialize the JSON response to a `HashMap<String, String>` for convenience and await on that.
51+
52+
### Adding headers to our request
53+
54+
To add headers to our request, we first make a request client and use that to add headers to our application.
55+
56+
use reqwest::header::HeaderMap;
57+
58+
#[tokio::main]
59+
async fn main() {
60+
...
61+
let client = reqwest::Client::new();
62+
let mut headers = HeaderMap::new();
63+
headers.insert("content-type", "application/json".parse().unwrap());
64+
65+
66+
use reqwest::header::HeaderMap;
67+
68+
#[tokio::main]
69+
async fn main() {
70+
...
71+
let client = reqwest::Client::new();
72+
let mut headers = HeaderMap::new();
73+
headers.insert("content-type", "application/json".parse().unwrap());
74+
headers.insert("Authorization", format!("Bearer {}", API_TOKEN).parse().unwrap());
75+
76+
let resp = client.get("<url>")
77+
.headers(headers)
78+
.send()
79+
.await?;
80+
81+
...
82+
}
83+
84+
Here’s what we did above:
85+
86+
* We create a request client to send our request.
87+
* We create a mutable instance of the `HeaderMap` Instance that is similar to `HashMap`
88+
* We insert our headers as key-value pairs to the header.
89+
* Also, we use `.parse().unwrap()` on the `&str` to convert the string type of the _header value_ type.
90+
* We then add our headers to the client request by using the `.headers()` method.
91+
* Also, one thing different from directly using the get method is that we have to call the `send` method on our request before awaiting on it.
92+
93+
### Sending post request with JSON body
94+
95+
We send the post request by using the `post` method on the _request client_ or directly from the library and use the `json` method to add body to the post request.
96+
97+
The body here is just a `HashMap` in rust.
98+
99+
...
100+
let mut body = HashMap::new();
101+
body.insert("username", "myusername");
102+
let resp = client.post("<url>")
103+
.json(&body)
104+
.send()
105+
.await?;
106+
...
107+
108+
### Deserializing the JSON response
109+
110+
We can deserialize the JSON response from the API by using the `json` method on the sent request, and get that into our preferred shape or type by calling it generically with that type.
111+
112+
One thing to note is that the **type must implement the Deserialize trait**.
113+
114+
First thing first we create the type we want our JSON response in and implement the deserialize trait on it, implementing the trait ourselves is tedious and unreliable, so we use the `serde` library that we imported before to do that for us.
115+
116+
use serde::Deserialize;
117+
118+
// deriving the Debug trait also to be able to print it
119+
#[derive(Debug, Deserialize)]
120+
struct APIResponse {
121+
message: String;
122+
error: String;
123+
}
124+
125+
We can then use the above struct to deserialize our response as:
126+
127+
...
128+
let resp_json = resp.json::<APIResponse>().await?;
129+
...
130+
131+
### References
132+
133+
* `reqwest` [library documentation](https://docs.rs/reqwest),
134+
* [Rust Async documentation](https://rust-lang.github.io/async-book),
135+
* [YouTube Tutorial by Tom](https://youtu.be/j9MsMYz9hBw).

0 commit comments

Comments
 (0)