Skip to main content

RequestBuilder

Struct RequestBuilder 

Source
pub struct RequestBuilder<'a, H> { /* private fields */ }
Expand description

Builder for constructing test requests with a fluent API.

Use the methods on TestClient to create a request builder, then chain configuration methods and call send to execute.

§Example

let response = client
    .post("/api/items")
    .header("Content-Type", "application/json")
    .body(r#"{"name": "Widget"}"#)
    .send();

Implementations§

Source§

impl<'a, H> RequestBuilder<'a, H>
where H: Handler + 'static,

Source

pub fn query(self, key: &str, value: &str) -> RequestBuilder<'a, H>

Sets a query string parameter.

Multiple calls append parameters.

§Example
client.get("/search").query("q", "rust").query("limit", "10").send()
Source

pub fn header( self, name: impl Into<String>, value: impl Into<Vec<u8>>, ) -> RequestBuilder<'a, H>

Sets a request header.

§Example
client.get("/api").header("Authorization", "Bearer token").send()
Examples found in repository?
examples/crud_api.rs (line 277)
252fn main() {
253    // Initialize the global store
254    *STORE.lock().unwrap() = Some(UserDb {
255        users: HashMap::new(),
256        next_id: 1,
257    });
258
259    println!("fastapi_rust CRUD API Example");
260    println!("=============================\n");
261
262    let app = App::builder()
263        .post("/users", create_user)
264        .get("/users", list_users)
265        .get("/users/{id}", get_user)
266        .put("/users/{id}", update_user)
267        .delete("/users/{id}", delete_user)
268        .build();
269
270    println!("App created with {} route(s)\n", app.route_count());
271    let client = TestClient::new(app);
272
273    // 1. Create users
274    println!("1. Create users");
275    let resp = client
276        .post("/users")
277        .header("content-type", "application/json")
278        .body(r#"{"name": "Alice", "email": "alice@example.com"}"#)
279        .send();
280    println!(
281        "   POST /users -> {} {}",
282        resp.status().as_u16(),
283        resp.text()
284    );
285    assert_eq!(resp.status().as_u16(), 201);
286
287    let resp = client
288        .post("/users")
289        .header("content-type", "application/json")
290        .body(r#"{"name": "Bob", "email": "bob@example.com"}"#)
291        .send();
292    println!(
293        "   POST /users -> {} {}",
294        resp.status().as_u16(),
295        resp.text()
296    );
297    assert_eq!(resp.status().as_u16(), 201);
298
299    // 2. List users
300    println!("\n2. List all users");
301    let resp = client.get("/users").send();
302    println!(
303        "   GET /users -> {} {}",
304        resp.status().as_u16(),
305        resp.text()
306    );
307    assert_eq!(resp.status().as_u16(), 200);
308
309    // 3. Get user by ID
310    println!("\n3. Get user by ID");
311    let resp = client.get("/users/1").send();
312    println!(
313        "   GET /users/1 -> {} {}",
314        resp.status().as_u16(),
315        resp.text()
316    );
317    assert_eq!(resp.status().as_u16(), 200);
318
319    // 4. Get nonexistent user
320    println!("\n4. Get nonexistent user");
321    let resp = client.get("/users/999").send();
322    println!(
323        "   GET /users/999 -> {} {}",
324        resp.status().as_u16(),
325        resp.text()
326    );
327    assert_eq!(resp.status().as_u16(), 404);
328
329    // 5. Update user
330    println!("\n5. Update user");
331    let resp = client
332        .put("/users/1")
333        .header("content-type", "application/json")
334        .body(r#"{"name": "Alice Smith", "email": "alice.smith@example.com"}"#)
335        .send();
336    println!(
337        "   PUT /users/1 -> {} {}",
338        resp.status().as_u16(),
339        resp.text()
340    );
341    assert_eq!(resp.status().as_u16(), 200);
342
343    // 6. Validation: empty name
344    println!("\n6. Validation error (empty name)");
345    let resp = client
346        .post("/users")
347        .header("content-type", "application/json")
348        .body(r#"{"name": "", "email": "bad@example.com"}"#)
349        .send();
350    println!(
351        "   POST /users -> {} {}",
352        resp.status().as_u16(),
353        resp.text()
354    );
355    assert_eq!(resp.status().as_u16(), 400);
356
357    // 7. Validation: invalid email
358    println!("\n7. Validation error (invalid email)");
359    let resp = client
360        .post("/users")
361        .header("content-type", "application/json")
362        .body(r#"{"name": "Charlie", "email": "not-an-email"}"#)
363        .send();
364    println!(
365        "   POST /users -> {} {}",
366        resp.status().as_u16(),
367        resp.text()
368    );
369    assert_eq!(resp.status().as_u16(), 400);
370
371    // 8. Wrong Content-Type
372    println!("\n8. Wrong Content-Type");
373    let resp = client
374        .post("/users")
375        .header("content-type", "text/plain")
376        .body(r#"{"name": "Dan", "email": "dan@example.com"}"#)
377        .send();
378    println!(
379        "   POST /users -> {} {}",
380        resp.status().as_u16(),
381        resp.text()
382    );
383    assert_eq!(resp.status().as_u16(), 415);
384
385    // 9. Delete user
386    println!("\n9. Delete user");
387    let resp = client.delete("/users/2").send();
388    println!("   DELETE /users/2 -> {}", resp.status().as_u16());
389    assert_eq!(resp.status().as_u16(), 204);
390
391    // 10. Verify deletion
392    println!("\n10. Verify deletion");
393    let resp = client.get("/users/2").send();
394    println!(
395        "   GET /users/2 -> {} {}",
396        resp.status().as_u16(),
397        resp.text()
398    );
399    assert_eq!(resp.status().as_u16(), 404);
400
401    let resp = client.get("/users").send();
402    println!(
403        "   GET /users -> {} {}",
404        resp.status().as_u16(),
405        resp.text()
406    );
407    assert_eq!(resp.status().as_u16(), 200);
408
409    println!("\nAll CRUD operations passed!");
410}
More examples
Hide additional examples
examples/auth_example.rs (line 312)
248fn main() {
249    println!("fastapi_rust Authentication Example");
250    println!("====================================\n");
251
252    // Build the application with public and protected routes
253    let app = App::builder()
254        // Public endpoints - accessible to everyone
255        .get("/public", public_handler)
256        // Login endpoint - returns a token
257        .post("/login", login_handler)
258        // Protected endpoint - requires valid bearer token
259        .get("/protected", protected_handler)
260        .build();
261
262    println!("App created with {} route(s)\n", app.route_count());
263
264    // Create a test client
265    let client = TestClient::new(app);
266
267    // =========================================================================
268    // Test 1: Public endpoint - no auth required
269    // =========================================================================
270    println!("1. Public endpoint - no auth required");
271    let response = client.get("/public").send();
272    println!(
273        "   GET /public -> {} {}",
274        response.status().as_u16(),
275        response.status().canonical_reason()
276    );
277    assert_eq!(response.status().as_u16(), 200);
278    assert!(response.text().contains("public endpoint"));
279
280    // =========================================================================
281    // Test 2: Protected endpoint - without token (should get 401)
282    // =========================================================================
283    println!("\n2. Protected endpoint - without token");
284    let response = client.get("/protected").send();
285    println!(
286        "   GET /protected -> {} {}",
287        response.status().as_u16(),
288        response.status().canonical_reason()
289    );
290    assert_eq!(
291        response.status().as_u16(),
292        401,
293        "Protected endpoint should return 401 without token"
294    );
295
296    // Check for WWW-Authenticate header
297    let has_www_auth = response
298        .headers()
299        .iter()
300        .any(|(name, value)| name == "www-authenticate" && value == b"Bearer");
301    assert!(
302        has_www_auth,
303        "401 response should include WWW-Authenticate: Bearer header"
304    );
305
306    // =========================================================================
307    // Test 3: Login endpoint - get a token
308    // =========================================================================
309    println!("\n3. Login endpoint - get a token");
310    let response = client
311        .post("/login")
312        .header("content-type", "application/json")
313        .body(r#"{"username":"test","password":"test123"}"#)
314        .send();
315    println!(
316        "   POST /login -> {} {}",
317        response.status().as_u16(),
318        response.status().canonical_reason()
319    );
320    assert_eq!(response.status().as_u16(), 200);
321
322    // Parse the response to get the token
323    let body: serde_json::Value = serde_json::from_str(response.text()).unwrap();
324    let token = body["access_token"].as_str().unwrap();
325    println!("   Token: {token}");
326    assert_eq!(token, SECRET_TOKEN);
327
328    // =========================================================================
329    // Test 4: Protected endpoint - with valid token (should get 200)
330    // =========================================================================
331    println!("\n4. Protected endpoint - with valid token");
332    let response = client
333        .get("/protected")
334        .header("authorization", format!("Bearer {SECRET_TOKEN}"))
335        .send();
336    println!(
337        "   GET /protected (Authorization: Bearer {}) -> {} {}",
338        SECRET_TOKEN,
339        response.status().as_u16(),
340        response.status().canonical_reason()
341    );
342    assert_eq!(
343        response.status().as_u16(),
344        200,
345        "Protected endpoint should return 200 with valid token"
346    );
347    assert!(response.text().contains("protected resource"));
348
349    // =========================================================================
350    // Test 5: Protected endpoint - with invalid token (should get 403)
351    // =========================================================================
352    println!("\n5. Protected endpoint - with invalid token");
353    let response = client
354        .get("/protected")
355        .header("authorization", "Bearer wrong_token")
356        .send();
357    println!(
358        "   GET /protected (Authorization: Bearer wrong_token) -> {} {}",
359        response.status().as_u16(),
360        response.status().canonical_reason()
361    );
362    assert_eq!(
363        response.status().as_u16(),
364        403,
365        "Protected endpoint should return 403 with invalid token"
366    );
367
368    // =========================================================================
369    // Test 6: Protected endpoint - with wrong auth scheme (should get 401)
370    // =========================================================================
371    println!("\n6. Protected endpoint - with wrong auth scheme");
372    let response = client
373        .get("/protected")
374        .header("authorization", "Basic dXNlcjpwYXNz")
375        .send();
376    println!(
377        "   GET /protected (Authorization: Basic ...) -> {} {}",
378        response.status().as_u16(),
379        response.status().canonical_reason()
380    );
381    assert_eq!(
382        response.status().as_u16(),
383        401,
384        "Protected endpoint should return 401 with wrong auth scheme"
385    );
386
387    // =========================================================================
388    // Test 7: Login with wrong Content-Type (should get 415)
389    // =========================================================================
390    println!("\n7. Login with wrong Content-Type");
391    let response = client
392        .post("/login")
393        .header("content-type", "text/plain")
394        .body("username=test&password=test123")
395        .send();
396    println!(
397        "   POST /login (Content-Type: text/plain) -> {} {}",
398        response.status().as_u16(),
399        response.status().canonical_reason()
400    );
401    assert_eq!(
402        response.status().as_u16(),
403        415,
404        "Login should return 415 with wrong Content-Type"
405    );
406
407    // =========================================================================
408    // Test 8: Token case sensitivity (lowercase 'bearer')
409    // =========================================================================
410    println!("\n8. Token case sensitivity (lowercase 'bearer')");
411    let response = client
412        .get("/protected")
413        .header("authorization", format!("bearer {SECRET_TOKEN}"))
414        .send();
415    println!(
416        "   GET /protected (Authorization: bearer {}) -> {} {}",
417        SECRET_TOKEN,
418        response.status().as_u16(),
419        response.status().canonical_reason()
420    );
421    assert_eq!(
422        response.status().as_u16(),
423        200,
424        "Bearer scheme should be case-insensitive (lowercase accepted)"
425    );
426
427    println!("\nAll authentication tests passed!");
428}
Source

pub fn header_str( self, name: impl Into<String>, value: &str, ) -> RequestBuilder<'a, H>

Sets a request header with a string value.

Source

pub fn body(self, body: impl Into<Vec<u8>>) -> RequestBuilder<'a, H>

Sets the request body as raw bytes.

§Example
client.post("/upload").body(b"binary data".to_vec()).send()
Examples found in repository?
examples/crud_api.rs (line 278)
252fn main() {
253    // Initialize the global store
254    *STORE.lock().unwrap() = Some(UserDb {
255        users: HashMap::new(),
256        next_id: 1,
257    });
258
259    println!("fastapi_rust CRUD API Example");
260    println!("=============================\n");
261
262    let app = App::builder()
263        .post("/users", create_user)
264        .get("/users", list_users)
265        .get("/users/{id}", get_user)
266        .put("/users/{id}", update_user)
267        .delete("/users/{id}", delete_user)
268        .build();
269
270    println!("App created with {} route(s)\n", app.route_count());
271    let client = TestClient::new(app);
272
273    // 1. Create users
274    println!("1. Create users");
275    let resp = client
276        .post("/users")
277        .header("content-type", "application/json")
278        .body(r#"{"name": "Alice", "email": "alice@example.com"}"#)
279        .send();
280    println!(
281        "   POST /users -> {} {}",
282        resp.status().as_u16(),
283        resp.text()
284    );
285    assert_eq!(resp.status().as_u16(), 201);
286
287    let resp = client
288        .post("/users")
289        .header("content-type", "application/json")
290        .body(r#"{"name": "Bob", "email": "bob@example.com"}"#)
291        .send();
292    println!(
293        "   POST /users -> {} {}",
294        resp.status().as_u16(),
295        resp.text()
296    );
297    assert_eq!(resp.status().as_u16(), 201);
298
299    // 2. List users
300    println!("\n2. List all users");
301    let resp = client.get("/users").send();
302    println!(
303        "   GET /users -> {} {}",
304        resp.status().as_u16(),
305        resp.text()
306    );
307    assert_eq!(resp.status().as_u16(), 200);
308
309    // 3. Get user by ID
310    println!("\n3. Get user by ID");
311    let resp = client.get("/users/1").send();
312    println!(
313        "   GET /users/1 -> {} {}",
314        resp.status().as_u16(),
315        resp.text()
316    );
317    assert_eq!(resp.status().as_u16(), 200);
318
319    // 4. Get nonexistent user
320    println!("\n4. Get nonexistent user");
321    let resp = client.get("/users/999").send();
322    println!(
323        "   GET /users/999 -> {} {}",
324        resp.status().as_u16(),
325        resp.text()
326    );
327    assert_eq!(resp.status().as_u16(), 404);
328
329    // 5. Update user
330    println!("\n5. Update user");
331    let resp = client
332        .put("/users/1")
333        .header("content-type", "application/json")
334        .body(r#"{"name": "Alice Smith", "email": "alice.smith@example.com"}"#)
335        .send();
336    println!(
337        "   PUT /users/1 -> {} {}",
338        resp.status().as_u16(),
339        resp.text()
340    );
341    assert_eq!(resp.status().as_u16(), 200);
342
343    // 6. Validation: empty name
344    println!("\n6. Validation error (empty name)");
345    let resp = client
346        .post("/users")
347        .header("content-type", "application/json")
348        .body(r#"{"name": "", "email": "bad@example.com"}"#)
349        .send();
350    println!(
351        "   POST /users -> {} {}",
352        resp.status().as_u16(),
353        resp.text()
354    );
355    assert_eq!(resp.status().as_u16(), 400);
356
357    // 7. Validation: invalid email
358    println!("\n7. Validation error (invalid email)");
359    let resp = client
360        .post("/users")
361        .header("content-type", "application/json")
362        .body(r#"{"name": "Charlie", "email": "not-an-email"}"#)
363        .send();
364    println!(
365        "   POST /users -> {} {}",
366        resp.status().as_u16(),
367        resp.text()
368    );
369    assert_eq!(resp.status().as_u16(), 400);
370
371    // 8. Wrong Content-Type
372    println!("\n8. Wrong Content-Type");
373    let resp = client
374        .post("/users")
375        .header("content-type", "text/plain")
376        .body(r#"{"name": "Dan", "email": "dan@example.com"}"#)
377        .send();
378    println!(
379        "   POST /users -> {} {}",
380        resp.status().as_u16(),
381        resp.text()
382    );
383    assert_eq!(resp.status().as_u16(), 415);
384
385    // 9. Delete user
386    println!("\n9. Delete user");
387    let resp = client.delete("/users/2").send();
388    println!("   DELETE /users/2 -> {}", resp.status().as_u16());
389    assert_eq!(resp.status().as_u16(), 204);
390
391    // 10. Verify deletion
392    println!("\n10. Verify deletion");
393    let resp = client.get("/users/2").send();
394    println!(
395        "   GET /users/2 -> {} {}",
396        resp.status().as_u16(),
397        resp.text()
398    );
399    assert_eq!(resp.status().as_u16(), 404);
400
401    let resp = client.get("/users").send();
402    println!(
403        "   GET /users -> {} {}",
404        resp.status().as_u16(),
405        resp.text()
406    );
407    assert_eq!(resp.status().as_u16(), 200);
408
409    println!("\nAll CRUD operations passed!");
410}
More examples
Hide additional examples
examples/auth_example.rs (line 313)
248fn main() {
249    println!("fastapi_rust Authentication Example");
250    println!("====================================\n");
251
252    // Build the application with public and protected routes
253    let app = App::builder()
254        // Public endpoints - accessible to everyone
255        .get("/public", public_handler)
256        // Login endpoint - returns a token
257        .post("/login", login_handler)
258        // Protected endpoint - requires valid bearer token
259        .get("/protected", protected_handler)
260        .build();
261
262    println!("App created with {} route(s)\n", app.route_count());
263
264    // Create a test client
265    let client = TestClient::new(app);
266
267    // =========================================================================
268    // Test 1: Public endpoint - no auth required
269    // =========================================================================
270    println!("1. Public endpoint - no auth required");
271    let response = client.get("/public").send();
272    println!(
273        "   GET /public -> {} {}",
274        response.status().as_u16(),
275        response.status().canonical_reason()
276    );
277    assert_eq!(response.status().as_u16(), 200);
278    assert!(response.text().contains("public endpoint"));
279
280    // =========================================================================
281    // Test 2: Protected endpoint - without token (should get 401)
282    // =========================================================================
283    println!("\n2. Protected endpoint - without token");
284    let response = client.get("/protected").send();
285    println!(
286        "   GET /protected -> {} {}",
287        response.status().as_u16(),
288        response.status().canonical_reason()
289    );
290    assert_eq!(
291        response.status().as_u16(),
292        401,
293        "Protected endpoint should return 401 without token"
294    );
295
296    // Check for WWW-Authenticate header
297    let has_www_auth = response
298        .headers()
299        .iter()
300        .any(|(name, value)| name == "www-authenticate" && value == b"Bearer");
301    assert!(
302        has_www_auth,
303        "401 response should include WWW-Authenticate: Bearer header"
304    );
305
306    // =========================================================================
307    // Test 3: Login endpoint - get a token
308    // =========================================================================
309    println!("\n3. Login endpoint - get a token");
310    let response = client
311        .post("/login")
312        .header("content-type", "application/json")
313        .body(r#"{"username":"test","password":"test123"}"#)
314        .send();
315    println!(
316        "   POST /login -> {} {}",
317        response.status().as_u16(),
318        response.status().canonical_reason()
319    );
320    assert_eq!(response.status().as_u16(), 200);
321
322    // Parse the response to get the token
323    let body: serde_json::Value = serde_json::from_str(response.text()).unwrap();
324    let token = body["access_token"].as_str().unwrap();
325    println!("   Token: {token}");
326    assert_eq!(token, SECRET_TOKEN);
327
328    // =========================================================================
329    // Test 4: Protected endpoint - with valid token (should get 200)
330    // =========================================================================
331    println!("\n4. Protected endpoint - with valid token");
332    let response = client
333        .get("/protected")
334        .header("authorization", format!("Bearer {SECRET_TOKEN}"))
335        .send();
336    println!(
337        "   GET /protected (Authorization: Bearer {}) -> {} {}",
338        SECRET_TOKEN,
339        response.status().as_u16(),
340        response.status().canonical_reason()
341    );
342    assert_eq!(
343        response.status().as_u16(),
344        200,
345        "Protected endpoint should return 200 with valid token"
346    );
347    assert!(response.text().contains("protected resource"));
348
349    // =========================================================================
350    // Test 5: Protected endpoint - with invalid token (should get 403)
351    // =========================================================================
352    println!("\n5. Protected endpoint - with invalid token");
353    let response = client
354        .get("/protected")
355        .header("authorization", "Bearer wrong_token")
356        .send();
357    println!(
358        "   GET /protected (Authorization: Bearer wrong_token) -> {} {}",
359        response.status().as_u16(),
360        response.status().canonical_reason()
361    );
362    assert_eq!(
363        response.status().as_u16(),
364        403,
365        "Protected endpoint should return 403 with invalid token"
366    );
367
368    // =========================================================================
369    // Test 6: Protected endpoint - with wrong auth scheme (should get 401)
370    // =========================================================================
371    println!("\n6. Protected endpoint - with wrong auth scheme");
372    let response = client
373        .get("/protected")
374        .header("authorization", "Basic dXNlcjpwYXNz")
375        .send();
376    println!(
377        "   GET /protected (Authorization: Basic ...) -> {} {}",
378        response.status().as_u16(),
379        response.status().canonical_reason()
380    );
381    assert_eq!(
382        response.status().as_u16(),
383        401,
384        "Protected endpoint should return 401 with wrong auth scheme"
385    );
386
387    // =========================================================================
388    // Test 7: Login with wrong Content-Type (should get 415)
389    // =========================================================================
390    println!("\n7. Login with wrong Content-Type");
391    let response = client
392        .post("/login")
393        .header("content-type", "text/plain")
394        .body("username=test&password=test123")
395        .send();
396    println!(
397        "   POST /login (Content-Type: text/plain) -> {} {}",
398        response.status().as_u16(),
399        response.status().canonical_reason()
400    );
401    assert_eq!(
402        response.status().as_u16(),
403        415,
404        "Login should return 415 with wrong Content-Type"
405    );
406
407    // =========================================================================
408    // Test 8: Token case sensitivity (lowercase 'bearer')
409    // =========================================================================
410    println!("\n8. Token case sensitivity (lowercase 'bearer')");
411    let response = client
412        .get("/protected")
413        .header("authorization", format!("bearer {SECRET_TOKEN}"))
414        .send();
415    println!(
416        "   GET /protected (Authorization: bearer {}) -> {} {}",
417        SECRET_TOKEN,
418        response.status().as_u16(),
419        response.status().canonical_reason()
420    );
421    assert_eq!(
422        response.status().as_u16(),
423        200,
424        "Bearer scheme should be case-insensitive (lowercase accepted)"
425    );
426
427    println!("\nAll authentication tests passed!");
428}
Source

pub fn body_str(self, body: &str) -> RequestBuilder<'a, H>

Sets the request body as a string.

Source

pub fn json<T>(self, value: &T) -> RequestBuilder<'a, H>
where T: Serialize,

Sets the request body as JSON.

Automatically sets the Content-Type header to application/json.

§Example
#[derive(Serialize)]
struct CreateUser { name: String }

client.post("/users").json(&CreateUser { name: "Alice".into() }).send()
Source

pub fn cookie(self, name: &str, value: &str) -> RequestBuilder<'a, H>

Sets a cookie for this request only.

This does not affect the client’s cookie jar.

Source

pub fn send(self) -> TestResponse

Sends the request and returns the response.

§Example
let response = client.get("/").send();
Examples found in repository?
examples/hello_world.rs (line 84)
51fn main() {
52    println!("fastapi_rust Hello World Example");
53    println!("================================\n");
54
55    // Build the application
56    //
57    // App::builder() creates a new application builder that lets you:
58    // - Add routes for different HTTP methods
59    // - Configure middleware
60    // - Set application state
61    // - Define exception handlers
62    let app = App::builder()
63        // Register a GET handler for the root path "/"
64        //
65        // This is equivalent to:
66        //   @app.get("/")
67        //   def hello():
68        //       return "Hello, World!"
69        // in Python FastAPI
70        .get("/", hello_handler)
71        // Build the final immutable App
72        .build();
73
74    println!("App created with {} route(s)\n", app.route_count());
75
76    // Create a test client to make requests to our app
77    //
78    // TestClient wraps any Handler (including App) and provides
79    // a convenient API for making HTTP requests in tests.
80    let client = TestClient::new(app);
81
82    // Make a GET request to "/"
83    println!("Making request: GET /");
84    let response = client.get("/").send();
85
86    // Check the response
87    println!(
88        "GET / -> {} {}",
89        response.status().as_u16(),
90        response.status().canonical_reason()
91    );
92    println!("Response: {}\n", response.text());
93
94    // Verify success
95    assert_eq!(response.status().as_u16(), 200);
96    assert_eq!(response.text(), "Hello, World!");
97
98    // Try a path that doesn't exist - should get 404
99    println!("Making request: GET /not-found");
100    let response = client.get("/not-found").send();
101    println!(
102        "GET /not-found -> {} {}",
103        response.status().as_u16(),
104        response.status().canonical_reason()
105    );
106    assert_eq!(response.status().as_u16(), 404);
107
108    println!("\nAll assertions passed!");
109}
More examples
Hide additional examples
examples/getting_started.rs (line 37)
24fn main() {
25    println!("Getting Started Guide - Code Validation\n");
26
27    // === Basic App Example ===
28    println!("1. Basic app with two routes:");
29    let app = App::builder()
30        .get("/", hello)
31        .get("/health", health)
32        .build();
33
34    println!("   Routes: {}", app.route_count());
35    let client = TestClient::new(app);
36
37    let response = client.get("/").send();
38    println!(
39        "   GET / -> {} ({})",
40        response.status().as_u16(),
41        response.text()
42    );
43    assert_eq!(response.status().as_u16(), 200);
44    assert_eq!(response.text(), "Hello, World!");
45
46    let response = client.get("/health").send();
47    println!(
48        "   GET /health -> {} ({})",
49        response.status().as_u16(),
50        response.text()
51    );
52    assert_eq!(response.status().as_u16(), 200);
53
54    // === App with Middleware ===
55    println!("\n2. App with middleware:");
56    let app = App::builder()
57        .middleware(RequestIdMiddleware::new())
58        .middleware(SecurityHeaders::new())
59        .get("/", hello)
60        .build();
61
62    let client = TestClient::new(app);
63    let response = client.get("/").send();
64    println!("   GET / -> {}", response.status().as_u16());
65    assert_eq!(response.status().as_u16(), 200);
66
67    // === App with Configuration ===
68    println!("\n3. App with configuration:");
69    let config = AppConfig::new()
70        .name("My API")
71        .version("1.0.0")
72        .debug(true)
73        .max_body_size(10 * 1024 * 1024)
74        .request_timeout_ms(30_000);
75
76    let app = App::builder().config(config).get("/", hello).build();
77
78    println!("   App name: {}", app.config().name);
79    println!("   Version: {}", app.config().version);
80    assert_eq!(app.config().name, "My API");
81    assert_eq!(app.config().version, "1.0.0");
82
83    // === 404 for unknown routes ===
84    println!("\n4. 404 for unknown routes:");
85    let app = App::builder().get("/", hello).build();
86
87    let client = TestClient::new(app);
88    let response = client.get("/nonexistent").send();
89    println!("   GET /nonexistent -> {}", response.status().as_u16());
90    assert_eq!(response.status().as_u16(), 404);
91
92    println!("\nAll getting started examples validated successfully!");
93}
examples/crud_api.rs (line 279)
252fn main() {
253    // Initialize the global store
254    *STORE.lock().unwrap() = Some(UserDb {
255        users: HashMap::new(),
256        next_id: 1,
257    });
258
259    println!("fastapi_rust CRUD API Example");
260    println!("=============================\n");
261
262    let app = App::builder()
263        .post("/users", create_user)
264        .get("/users", list_users)
265        .get("/users/{id}", get_user)
266        .put("/users/{id}", update_user)
267        .delete("/users/{id}", delete_user)
268        .build();
269
270    println!("App created with {} route(s)\n", app.route_count());
271    let client = TestClient::new(app);
272
273    // 1. Create users
274    println!("1. Create users");
275    let resp = client
276        .post("/users")
277        .header("content-type", "application/json")
278        .body(r#"{"name": "Alice", "email": "alice@example.com"}"#)
279        .send();
280    println!(
281        "   POST /users -> {} {}",
282        resp.status().as_u16(),
283        resp.text()
284    );
285    assert_eq!(resp.status().as_u16(), 201);
286
287    let resp = client
288        .post("/users")
289        .header("content-type", "application/json")
290        .body(r#"{"name": "Bob", "email": "bob@example.com"}"#)
291        .send();
292    println!(
293        "   POST /users -> {} {}",
294        resp.status().as_u16(),
295        resp.text()
296    );
297    assert_eq!(resp.status().as_u16(), 201);
298
299    // 2. List users
300    println!("\n2. List all users");
301    let resp = client.get("/users").send();
302    println!(
303        "   GET /users -> {} {}",
304        resp.status().as_u16(),
305        resp.text()
306    );
307    assert_eq!(resp.status().as_u16(), 200);
308
309    // 3. Get user by ID
310    println!("\n3. Get user by ID");
311    let resp = client.get("/users/1").send();
312    println!(
313        "   GET /users/1 -> {} {}",
314        resp.status().as_u16(),
315        resp.text()
316    );
317    assert_eq!(resp.status().as_u16(), 200);
318
319    // 4. Get nonexistent user
320    println!("\n4. Get nonexistent user");
321    let resp = client.get("/users/999").send();
322    println!(
323        "   GET /users/999 -> {} {}",
324        resp.status().as_u16(),
325        resp.text()
326    );
327    assert_eq!(resp.status().as_u16(), 404);
328
329    // 5. Update user
330    println!("\n5. Update user");
331    let resp = client
332        .put("/users/1")
333        .header("content-type", "application/json")
334        .body(r#"{"name": "Alice Smith", "email": "alice.smith@example.com"}"#)
335        .send();
336    println!(
337        "   PUT /users/1 -> {} {}",
338        resp.status().as_u16(),
339        resp.text()
340    );
341    assert_eq!(resp.status().as_u16(), 200);
342
343    // 6. Validation: empty name
344    println!("\n6. Validation error (empty name)");
345    let resp = client
346        .post("/users")
347        .header("content-type", "application/json")
348        .body(r#"{"name": "", "email": "bad@example.com"}"#)
349        .send();
350    println!(
351        "   POST /users -> {} {}",
352        resp.status().as_u16(),
353        resp.text()
354    );
355    assert_eq!(resp.status().as_u16(), 400);
356
357    // 7. Validation: invalid email
358    println!("\n7. Validation error (invalid email)");
359    let resp = client
360        .post("/users")
361        .header("content-type", "application/json")
362        .body(r#"{"name": "Charlie", "email": "not-an-email"}"#)
363        .send();
364    println!(
365        "   POST /users -> {} {}",
366        resp.status().as_u16(),
367        resp.text()
368    );
369    assert_eq!(resp.status().as_u16(), 400);
370
371    // 8. Wrong Content-Type
372    println!("\n8. Wrong Content-Type");
373    let resp = client
374        .post("/users")
375        .header("content-type", "text/plain")
376        .body(r#"{"name": "Dan", "email": "dan@example.com"}"#)
377        .send();
378    println!(
379        "   POST /users -> {} {}",
380        resp.status().as_u16(),
381        resp.text()
382    );
383    assert_eq!(resp.status().as_u16(), 415);
384
385    // 9. Delete user
386    println!("\n9. Delete user");
387    let resp = client.delete("/users/2").send();
388    println!("   DELETE /users/2 -> {}", resp.status().as_u16());
389    assert_eq!(resp.status().as_u16(), 204);
390
391    // 10. Verify deletion
392    println!("\n10. Verify deletion");
393    let resp = client.get("/users/2").send();
394    println!(
395        "   GET /users/2 -> {} {}",
396        resp.status().as_u16(),
397        resp.text()
398    );
399    assert_eq!(resp.status().as_u16(), 404);
400
401    let resp = client.get("/users").send();
402    println!(
403        "   GET /users -> {} {}",
404        resp.status().as_u16(),
405        resp.text()
406    );
407    assert_eq!(resp.status().as_u16(), 200);
408
409    println!("\nAll CRUD operations passed!");
410}
examples/auth_example.rs (line 271)
248fn main() {
249    println!("fastapi_rust Authentication Example");
250    println!("====================================\n");
251
252    // Build the application with public and protected routes
253    let app = App::builder()
254        // Public endpoints - accessible to everyone
255        .get("/public", public_handler)
256        // Login endpoint - returns a token
257        .post("/login", login_handler)
258        // Protected endpoint - requires valid bearer token
259        .get("/protected", protected_handler)
260        .build();
261
262    println!("App created with {} route(s)\n", app.route_count());
263
264    // Create a test client
265    let client = TestClient::new(app);
266
267    // =========================================================================
268    // Test 1: Public endpoint - no auth required
269    // =========================================================================
270    println!("1. Public endpoint - no auth required");
271    let response = client.get("/public").send();
272    println!(
273        "   GET /public -> {} {}",
274        response.status().as_u16(),
275        response.status().canonical_reason()
276    );
277    assert_eq!(response.status().as_u16(), 200);
278    assert!(response.text().contains("public endpoint"));
279
280    // =========================================================================
281    // Test 2: Protected endpoint - without token (should get 401)
282    // =========================================================================
283    println!("\n2. Protected endpoint - without token");
284    let response = client.get("/protected").send();
285    println!(
286        "   GET /protected -> {} {}",
287        response.status().as_u16(),
288        response.status().canonical_reason()
289    );
290    assert_eq!(
291        response.status().as_u16(),
292        401,
293        "Protected endpoint should return 401 without token"
294    );
295
296    // Check for WWW-Authenticate header
297    let has_www_auth = response
298        .headers()
299        .iter()
300        .any(|(name, value)| name == "www-authenticate" && value == b"Bearer");
301    assert!(
302        has_www_auth,
303        "401 response should include WWW-Authenticate: Bearer header"
304    );
305
306    // =========================================================================
307    // Test 3: Login endpoint - get a token
308    // =========================================================================
309    println!("\n3. Login endpoint - get a token");
310    let response = client
311        .post("/login")
312        .header("content-type", "application/json")
313        .body(r#"{"username":"test","password":"test123"}"#)
314        .send();
315    println!(
316        "   POST /login -> {} {}",
317        response.status().as_u16(),
318        response.status().canonical_reason()
319    );
320    assert_eq!(response.status().as_u16(), 200);
321
322    // Parse the response to get the token
323    let body: serde_json::Value = serde_json::from_str(response.text()).unwrap();
324    let token = body["access_token"].as_str().unwrap();
325    println!("   Token: {token}");
326    assert_eq!(token, SECRET_TOKEN);
327
328    // =========================================================================
329    // Test 4: Protected endpoint - with valid token (should get 200)
330    // =========================================================================
331    println!("\n4. Protected endpoint - with valid token");
332    let response = client
333        .get("/protected")
334        .header("authorization", format!("Bearer {SECRET_TOKEN}"))
335        .send();
336    println!(
337        "   GET /protected (Authorization: Bearer {}) -> {} {}",
338        SECRET_TOKEN,
339        response.status().as_u16(),
340        response.status().canonical_reason()
341    );
342    assert_eq!(
343        response.status().as_u16(),
344        200,
345        "Protected endpoint should return 200 with valid token"
346    );
347    assert!(response.text().contains("protected resource"));
348
349    // =========================================================================
350    // Test 5: Protected endpoint - with invalid token (should get 403)
351    // =========================================================================
352    println!("\n5. Protected endpoint - with invalid token");
353    let response = client
354        .get("/protected")
355        .header("authorization", "Bearer wrong_token")
356        .send();
357    println!(
358        "   GET /protected (Authorization: Bearer wrong_token) -> {} {}",
359        response.status().as_u16(),
360        response.status().canonical_reason()
361    );
362    assert_eq!(
363        response.status().as_u16(),
364        403,
365        "Protected endpoint should return 403 with invalid token"
366    );
367
368    // =========================================================================
369    // Test 6: Protected endpoint - with wrong auth scheme (should get 401)
370    // =========================================================================
371    println!("\n6. Protected endpoint - with wrong auth scheme");
372    let response = client
373        .get("/protected")
374        .header("authorization", "Basic dXNlcjpwYXNz")
375        .send();
376    println!(
377        "   GET /protected (Authorization: Basic ...) -> {} {}",
378        response.status().as_u16(),
379        response.status().canonical_reason()
380    );
381    assert_eq!(
382        response.status().as_u16(),
383        401,
384        "Protected endpoint should return 401 with wrong auth scheme"
385    );
386
387    // =========================================================================
388    // Test 7: Login with wrong Content-Type (should get 415)
389    // =========================================================================
390    println!("\n7. Login with wrong Content-Type");
391    let response = client
392        .post("/login")
393        .header("content-type", "text/plain")
394        .body("username=test&password=test123")
395        .send();
396    println!(
397        "   POST /login (Content-Type: text/plain) -> {} {}",
398        response.status().as_u16(),
399        response.status().canonical_reason()
400    );
401    assert_eq!(
402        response.status().as_u16(),
403        415,
404        "Login should return 415 with wrong Content-Type"
405    );
406
407    // =========================================================================
408    // Test 8: Token case sensitivity (lowercase 'bearer')
409    // =========================================================================
410    println!("\n8. Token case sensitivity (lowercase 'bearer')");
411    let response = client
412        .get("/protected")
413        .header("authorization", format!("bearer {SECRET_TOKEN}"))
414        .send();
415    println!(
416        "   GET /protected (Authorization: bearer {}) -> {} {}",
417        SECRET_TOKEN,
418        response.status().as_u16(),
419        response.status().canonical_reason()
420    );
421    assert_eq!(
422        response.status().as_u16(),
423        200,
424        "Bearer scheme should be case-insensitive (lowercase accepted)"
425    );
426
427    println!("\nAll authentication tests passed!");
428}

Auto Trait Implementations§

§

impl<'a, H> Freeze for RequestBuilder<'a, H>

§

impl<'a, H> !RefUnwindSafe for RequestBuilder<'a, H>

§

impl<'a, H> Send for RequestBuilder<'a, H>
where H: Sync + Send,

§

impl<'a, H> Sync for RequestBuilder<'a, H>
where H: Sync + Send,

§

impl<'a, H> Unpin for RequestBuilder<'a, H>

§

impl<'a, H> !UnwindSafe for RequestBuilder<'a, H>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, _span: NoopSpan) -> Self

Instruments this future with a span (no-op when disabled).
Source§

fn in_current_span(self) -> Self

Instruments this future with the current span (no-op when disabled).
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ResponseProduces<T> for T