Search code examples
rustreqwestrust-axum

Error Handling with Reqwest and Axum handlers?


What's the correct/proper method for error handling using reqwest to call an external API in an Axum handler?

Using the ? operator to try and handle errors, as in the Reqwest docs, causes the compiler to throw an error because my handler is no longer a valid Axum handler.

But without using ?, even if the external API returns an error (e.g. receiving a 400), I end up in the Ok branch of my match, causing the status codes from my API to always be 200.

How can I refactor the following code to properly handle errors coming back from the external API, return JSON responses, and produce a valid Axum handler, so that when a user makes a bad request my API responds with a proper status code, as well?

async fn get_weather(
    location_query: Query<WeatherQuery>,
) -> Result<Json<serde_json::Value>, Json<serde_json::Value>> {
    let location_query = &location_query.location;
    let key = std::env::var("WEATHER_STACK_API_KEY").unwrap();
    let request_url = format!(
        "http://api.weatherstack.com/current?access_key={}&query={}&units=f",
        key, location_query
    );
    let res = reqwest::get(request_url).await;
    println!("res: {:?}", res);
    match res {
        // the response from the reqwest::get() call is always ok, even if the user
        // makes a bad request (e.g. "?location=asdf1234")
        // since I need to handle that response here, my API is always returning 200
        Ok(response) => {
            let res_json = response.json::<Weather>().await;
            match res_json {
                Ok(res) => Ok(Json(serde_json::json!({
                    "data": res,
                    "success": true
                }))),
                Err(_e) => Err(Json(serde_json::json!({
                    "error": {
                        "message": "Request to Weather Stack Failed!",
                    },
                    "success": false
                }))),
            }
        }
        Err(_e) => Err(Json(serde_json::json!({
            "error": {
                "message": "Internal Server Error. Request failed.",
            },
            "success": false
        }))),
    }
}

Solution

  • You can probably convert most errors from any failable function to anyhow Error and then use it in your main handler. There is even an example in axum repository that might be a useful consideration.