graphql-rust / juniper

GraphQL server library for Rust
Other
5.7k stars 421 forks source link

Error handling and logging with juniper #876

Closed ziimakc closed 3 years ago

ziimakc commented 3 years ago

In my opinion errors should never be send as is to client and possible leak some sensitive information.

Question 1: is there something like formatError in apollo that can be used to check if error was handled?

Question 2: following error handling example how can i log errors on backend? Doing something like this:

.map_err(|err| ApiError::DBClient(&err))?;
.map_err(|_| ApiError::Msg("Custom error msg".to_string()))?;

My attempt:

pub enum ApiError {
    DBClient(&'static dyn Display),
    Msg(String),
}

impl<S: ScalarValue> IntoFieldError<S> for ApiError {
    fn into_field_error(self) -> FieldError<S> {
        match self {
            ApiError::DBClient(err) => {
                error!("{}", err);

                FieldError::new(
                    "Database error, sorry :(",
                    graphql_value!({
                        "type": "DB"
                    }),
                )
            }
            ApiError::Msg(msg) => FieldError::new(
                msg,
                graphql_value!({
                    "type": "Custom"
                }),
            ),
        }
    }
}

Give me some confusing error:

future cannot be sent between threads safely
the trait `std::marker::Sync` is not implemented for `(dyn std::fmt::Display + 'static)`
required for the cast to the object type `dyn std::future::Future<Output = std::result::Result<juniper::Value<__S>, juniper::FieldError<__S>>> + std::marker::Send`
LegNeato commented 3 years ago

I'm not super familiar with Apollo, but I don't see why you would need formatError. You should be able to run execute and do whatever you want with the result (log it, return it, etc). Are you talking about an http integration crate where you need hooks instead of juniper itself?

For the other issue you need your type to be send...display is not enough. I'd suggest using anyhow and thiserror as they can help you with the boilerplate.