clawspec_core/_tutorial/
chapter_2.rs

1//! # Chapter 2: Request Building
2//!
3//! This chapter covers building more complex requests with POST, path parameters,
4//! and query parameters.
5//!
6//! ## POST Requests with JSON Body
7//!
8//! To send JSON data, use the `.json()` method. Your request type needs
9//! [`Serialize`][serde::Serialize] and [`ToSchema`][utoipa::ToSchema]:
10//!
11//! ```rust,no_run
12//! use clawspec_core::ApiClient;
13//! use serde::{Deserialize, Serialize};
14//! use utoipa::ToSchema;
15//!
16//! #[derive(Serialize, ToSchema)]
17//! struct CreateUser {
18//!     name: String,
19//!     email: String,
20//! }
21//!
22//! #[derive(Deserialize, ToSchema)]
23//! struct User {
24//!     id: u64,
25//!     name: String,
26//!     email: String,
27//! }
28//!
29//! # #[tokio::main]
30//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
31//! let mut client = ApiClient::builder()
32//!     .with_host("api.example.com")
33//!     .build()?;
34//!
35//! let new_user = CreateUser {
36//!     name: "Alice".to_string(),
37//!     email: "alice@example.com".to_string(),
38//! };
39//!
40//! let created: User = client
41//!     .post("/users")?
42//!     .json(&new_user)?     // Serialize as JSON
43//!     .await?
44//!     .as_json()
45//!     .await?;
46//!
47//! println!("Created user with ID: {}", created.id);
48//! # Ok(())
49//! # }
50//! ```
51//!
52//! Both request and response schemas are captured automatically.
53//!
54//! ## Path Parameters
55//!
56//! Use [`CallPath`][crate::CallPath] for templated paths with parameters:
57//!
58//! ```rust,no_run
59//! use clawspec_core::{ApiClient, CallPath, ParamValue};
60//! # use serde::Deserialize;
61//! # use utoipa::ToSchema;
62//! # #[derive(Deserialize, ToSchema)]
63//! # struct User { id: u64 }
64//!
65//! # #[tokio::main]
66//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
67//! # let mut client = ApiClient::builder().build()?;
68//! // Define path with parameter placeholder
69//! let path = CallPath::from("/users/{user_id}")
70//!     .add_param("user_id", ParamValue::new(123));
71//!
72//! let user: User = client
73//!     .get(path)?
74//!     .await?
75//!     .as_json()
76//!     .await?;
77//! # Ok(())
78//! # }
79//! ```
80//!
81//! The generated OpenAPI will show `/users/{user_id}` with the parameter documented.
82//!
83//! ## Query Parameters
84//!
85//! Use [`CallQuery`][crate::CallQuery] for query string parameters:
86//!
87//! ```rust,no_run
88//! use clawspec_core::{ApiClient, CallQuery};
89//! # use serde::Deserialize;
90//! # use utoipa::ToSchema;
91//! # #[derive(Deserialize, ToSchema)]
92//! # struct UserList { users: Vec<String> }
93//!
94//! # #[tokio::main]
95//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
96//! # let mut client = ApiClient::builder().build()?;
97//! let query = CallQuery::new()
98//!     .add_param("page", 1)
99//!     .add_param("limit", 20)
100//!     .add_param("sort", "name");
101//!
102//! let users: UserList = client
103//!     .get("/users")?
104//!     .with_query(query)
105//!     .await?
106//!     .as_json()
107//!     .await?;
108//! # Ok(())
109//! # }
110//! ```
111//!
112//! This generates a request to `/users?page=1&limit=20&sort=name`.
113//!
114//! ## Combining Path and Query Parameters
115//!
116//! You can use both together:
117//!
118//! ```rust,no_run
119//! use clawspec_core::{ApiClient, CallPath, CallQuery, ParamValue};
120//! # use serde::Deserialize;
121//! # use utoipa::ToSchema;
122//! # #[derive(Deserialize, ToSchema)]
123//! # struct Posts { posts: Vec<String> }
124//!
125//! # #[tokio::main]
126//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
127//! # let mut client = ApiClient::builder().build()?;
128//! let path = CallPath::from("/users/{user_id}/posts")
129//!     .add_param("user_id", ParamValue::new(42));
130//!
131//! let query = CallQuery::new()
132//!     .add_param("status", "published")
133//!     .add_param("limit", 10);
134//!
135//! let posts: Posts = client
136//!     .get(path)?
137//!     .with_query(query)
138//!     .await?
139//!     .as_json()
140//!     .await?;
141//! # Ok(())
142//! # }
143//! ```
144//!
145//! ## Other HTTP Methods
146//!
147//! All standard HTTP methods are supported:
148//!
149//! ```rust,no_run
150//! use clawspec_core::{ApiClient, CallPath, ParamValue};
151//! # use serde::{Serialize, Deserialize};
152//! # use utoipa::ToSchema;
153//! # #[derive(Serialize, ToSchema)]
154//! # struct UpdateUser { name: String }
155//! # #[derive(Serialize, ToSchema)]
156//! # struct PatchUser { name: Option<String> }
157//! # #[derive(Deserialize, ToSchema)]
158//! # struct User { id: u64 }
159//!
160//! # #[tokio::main]
161//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
162//! # let mut client = ApiClient::builder().build()?;
163//! let path = CallPath::from("/users/{id}").add_param("id", ParamValue::new(1));
164//!
165//! // PUT - Full replacement
166//! client.put(path.clone())?
167//!     .json(&UpdateUser { name: "Bob".to_string() })?
168//!     .await?
169//!     .as_empty()
170//!     .await?;
171//!
172//! // PATCH - Partial update
173//! let updated: User = client.patch(path.clone())?
174//!     .json(&PatchUser { name: Some("Robert".to_string()) })?
175//!     .await?
176//!     .as_json()
177//!     .await?;
178//!
179//! // DELETE
180//! client.delete(path)?
181//!     .await?
182//!     .as_empty()
183//!     .await?;
184//! # Ok(())
185//! # }
186//! ```
187//!
188//! ## Key Points
189//!
190//! - Use `.json(&data)?` to send JSON request bodies
191//! - [`CallPath`][crate::CallPath] handles path parameters like `/users/{id}`
192//! - [`CallQuery`][crate::CallQuery] handles query parameters
193//! - All HTTP methods are available: `get`, `post`, `put`, `patch`, `delete`
194//!
195//! Next: [Chapter 3: Response Handling][super::chapter_3] - Learn about different
196//! response handling strategies.