Module chapter_3

Module chapter_3 

Source
Expand description

§Chapter 3: Response Handling

This chapter covers the various ways to handle API responses, including error handling patterns.

§Response Methods Overview

After sending a request, you have several options for handling the response:

MethodReturnsUse Case
as_json::<T>()TStandard JSON response
as_optional_json::<T>()Option<T>Resource that may not exist (404 → None)
as_result_json::<T, E>()Result<T, E>API with typed error responses
as_result_option_json::<T, E>()Result<Option<T>, E>Combined: 404 → Ok(None), errors → Err
as_raw()RawResultAccess status code and raw body
as_empty()()Responses with no body (204, etc.)
as_text()StringPlain text responses

§Standard JSON Response

The most common pattern - parse JSON and fail on errors:

let user: User = client
    .get("/users/123")?
    .await?
    .as_json()
    .await?;

§Optional JSON (404 as None)

Use as_optional_json() when a resource might not exist:

let user: Option<User> = client
    .get("/users/999")?
    .add_expected_status(404)  // Tell client 404 is expected
    .await?
    .as_optional_json()
    .await?;

match user {
    Some(u) => println!("Found: {}", u.name),
    None => println!("User not found"),
}

§Result JSON (Typed Errors)

When your API returns structured error responses:

#[derive(Debug, Deserialize, ToSchema)]
struct ApiError {
    code: String,
    message: String,
}

let result: Result<User, ApiError> = client
    .get("/users/123")?
    .add_expected_status(404)
    .await?
    .as_result_json()
    .await?;

match result {
    Ok(user) => println!("Got user: {:?}", user),
    Err(error) => println!("API error: {} - {}", error.code, error.message),
}

§Result Option JSON (404 as Ok(None))

Combines optional resources with typed errors:

// 2xx → Ok(Some(T))
// 404 → Ok(None)
// Other 4xx/5xx → Err(E)
let result: Result<Option<User>, ApiError> = client
    .get("/users/maybe-exists")?
    .add_expected_status(404)
    .await?
    .as_result_option_json()
    .await?;

match result {
    Ok(Some(user)) => println!("Found: {}", user.name),
    Ok(None) => println!("Not found (but not an error)"),
    Err(e) => println!("Actual error: {}", e.message),
}

§Expected Status Codes

By default, Clawspec expects 2xx-4xx status codes. Use these methods to customize expectations:

use clawspec_core::{ApiClient, expected_status_codes};

// Add a single expected status
client.get("/users/123")?
    .add_expected_status(404)
    .await?;

// Use specific status code
client.post("/users")?
    .with_expected_status(201)
    .await?;

// Use the macro for complex patterns
client.get("/resource")?
    .with_expected_status_codes(expected_status_codes!(200, 201, 204))
    .await?;

// Ranges are supported
client.get("/resource")?
    .with_expected_status_codes(expected_status_codes!(200-299, 404))
    .await?;

§Raw Response Access

When you need full control over the response:

let raw = client
    .get("/health")?
    .await?
    .as_raw()
    .await?;

println!("Status: {}", raw.status_code());
println!("Body: {:?}", raw.text());

// Access as bytes
let bytes: Option<&[u8]> = raw.bytes();

§Error Handling

Clawspec uses ApiClientError for client-level errors:

use clawspec_core::{ApiClient, ApiClientError};

match client.get("/users/123")?.with_expected_status(200).await {
    Ok(response) => {
        // Handle success
    }
    Err(ApiClientError::UnexpectedStatusCode { status_code, body }) => {
        println!("Got status {}: {}", status_code, body);
    }
    Err(e) => {
        println!("Other error: {}", e);
    }
}

§Key Points

  • Choose the response method that matches your API’s behavior
  • Use add_expected_status() to tell Clawspec about expected non-2xx codes
  • as_optional_json() is great for “get or not found” patterns
  • as_result_json() captures typed error schemas in OpenAPI

Next: Chapter 4: Advanced Parameters - Headers, cookies, and parameter styles.