HTTP client in Rust: making requests correctly
Rust is a modern systems programming language that has gained popularity due to its performance and memory safety. However, many developers accustomed to Python or JavaScript wonder: how do you make HTTP requests in Rust? In this article, we'll walk through creating an HTTP client in Rust using the popular reqwest library. You'll learn how to make GET and POST requests, handle JSON, manage headers, and work with asynchrony. After reading, you'll be able to confidently integrate an HTTP client into your Rust projects.
1. Installation and environment setup
First, let's create a new Rust project and add the necessary dependencies. Open a terminal and run:
cargo new http_client_rustcd http_client_rustNow edit the Cargo.toml file, adding the following dependencies:
[dependencies]reqwest = { version = "0.11", features = ["json"] }tokio = { version = "1", features = ["full"] }serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"Let's break down each library:
- reqwest — the main HTTP client. The
jsonflag adds support for JSON serialization/deserialization. - tokio — an asynchronous runtime required for reqwest to work in async mode.
- serde and serde_json — for working with JSON structures.
After adding the dependencies, run cargo build to download and compile all libraries.
2. Basic GET request
Let's start with a simple GET request to a public API. We'll create a structure for the response and make a request to the JSONPlaceholder service:
use reqwest;use serde::Deserialize;
#[derive(Debug, Deserialize)]struct Post { userId: u32, id: u32, title: String, body: String,}
#[tokio::main]async fn main() -> Result<(), reqwest::Error> { let url = "https://jsonplaceholder.typicode.com/posts/1"; let response = reqwest::get(url).await?; let post: Post = response.json().await?; println!("{:#?}", post); Ok(())}Let's break down the code:
- The
#[derive(Debug, Deserialize)]attribute automatically generates traits for printing and deserialization. #[tokio::main]turns the function into an asynchronous entry point.reqwest::get()performs a GET request, and.awaitwaits for it to complete.response.json()converts the JSON response into our structure.
Run the code with the cargo run command. You'll see the post data in the console.
3. POST request with data sending
Now let's look at how to send data to a server. For a POST request, we need to create a structure for the data being sent and use the post() method:
use reqwest::Client;use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize)]struct NewPost { title: String, body: String, userId: u32,}
#[derive(Debug, Deserialize)]struct CreatedPost { id: u32, title: String, body: String, userId: u32,}
#[tokio::main]async fn main() -> Result<(), reqwest::Error> { let client = Client::new(); let new_post = NewPost { title: "My first post in Rust".to_string(), body: "This is the body of the post sent using reqwest".to_string(), userId: 1, }; let response = client .post("https://jsonplaceholder.typicode.com/posts") .json(&new_post) .send() .await?; let created_post: CreatedPost = response.json().await?; println!("Created post with ID: {}", created_post.id); println