Error Handling in Rust (Actix Web)
Tags: rust, actix, errors, logging
Published: 2022-06-14 08:38:51

Errors are part of software development. There are basically two types of errors in Rust i.e recoverable and unrecoverable. 

**Recoverable errors** have type `Result<T, E>` and **Unrecoverable errors** have `panic!` macro that stops execution.

Errors serve two main purposes:
* Control flow (i.e. determine what to do next);
* Reporting (e.g. investigating, after the fact, what went wrong).


We can also distinguish errors based on their location:
* Internal (i.e. a function calling another function within our application);
* At the edge (i.e. an API request that we failed to fulfill).

|              |        Internal        | At the edge   |
|:------------:|:----------------------:|---------------|
| Control Flow | Types, methods, fields |  Status codes |
|   Reporting  |       Logs/traces      | Response body |


You can refer more <a href="https://www.lpalmieri.com/posts/error-handling-rust/">here</a>

The first place where we are likely to get an error is `pool.get()` . When we are unable to get the pool instance whatever will be the reason like wrong credentials, database instance not running, etc.

Let's try how to handle it.

## Using Unwrap to create panic.
Creating Panic is useful during the prototyping phase when we are more focused on logic implementation. 

<a href="https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow/blob/master/src/api_handlers.rs">'/src/api_handlers.rs'</a>

```
pub async fn get_tags(state: web::Data<AppState>) ->impl Responder  {
    // Just not handle the error and let the system to Panic (unrecoverable error)
    let client = state.pool
        .get()
        .await.unwrap();
    let result = db::get_tags_unwrap(&client).await;

    match result {
        Ok(tags) => HttpResponse::Ok().json(tags),
        Err(_) => HttpResponse::InternalServerError().into(),
    }
}
```

Calling unwrap will create panic and the execution stops. So, we have discovered that handling errors like this are not ideal in the production server.

Let's move to another way

## Using the Result<T, E>

### Handling Single Error

The caller of the execution must be aware of whether the program was completed successfully or failed. For this use case, we can use simple `ResultSignal` enum

<a href="https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow/blob/master/src/error.rs">'./src/error.rs'</a>
```
pub enum ResultSignal<Success> {
    Ok(Success),
    Err
}
```

It will return Ok status on success and an Error on failure. It is helpful now that our user is aware that something mishappen has occurred. It is suitable for a single kind of error but our system consists of different services and they can fail in different ways.

**what is the reason? where has failure occurred?**
To answer this, we need to handle various errors according to our needs like database errors, filter errors, etc

### Handling Multiple Errors

Let's create an enum for different types of errors. For the sake of simplicity, we just considering two cases only Db Error and Not Found Error

<a href="https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow/blob/master/src/error.rs">'./src/error.rs'</a>
```
pub enum AppErrorType {
    DbError,
    NotFoundError,
}
```

Then we need to implement the **Debug and Display** trait. It is used to print errors using *println* command.

```
#[derive(Debug)]
pub enum AppErrorType {
    DbError,
    NotFoundError,
}

impl fmt::Display for AppErrorType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}
```

* **#[derive(Debug)]** :  This macro will enable the Debug trait for the enum AppErrorType.

* **fn fmt** : Display is for user-facing output.
* **write!** and **writeln!** are two macros that are used to emit the format string to a specified stream. This is used to prevent intermediate allocations of format strings and instead directly write the output.

Let's implement the **From** trait to convert from one type to another like PoolError to AppErrorType.

```
impl From<PoolError> for AppErrorType {
    fn from(_error: PoolError) -> AppErrorType {
        AppErrorType::DbError
    }
}
impl From<Error> for AppErrorType {
    fn from(_error: Error) -> AppErrorType {
        AppErrorType::DbError
    }
}
```

Since, we will be using Result<HttpResponse,AppErrorType>  return type in api handlers then we need overwrite the **ResponseError** Trait.

Actix provide two methods **error_response** and **status_code** to handle errors response.

```
impl ResponseError for AppErrorType {
    fn error_response(&self) -> HttpResponse {
        HttpResponse::build(self.status_code()).finish()
    }
}
```

Then, we need to change the return type from **impl Responder** or **HttpResponse** to **Result<HttpResponse,AppErrorType>**. 

We have used the `?` trait already implemented above in error.rs instead to unwrap.

<a href="https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow/blob/master/src/api_handlers.rs">'./src/api_handlers.rs'</a>
```
pub async fn get_tags(state: web::Data<AppState>) ->Result<HttpResponse,AppErrorType> {
    let client: Client = state.pool.get().await?;
    let result = db::get_tags(&client).await;
    result.map(|tags| HttpResponse::Ok().json(tags))
}
```

Instead of using unwrap in `client.prepare("select * from tag limit 10;").await.unwrap()`, we can now use the `?` as we have implemented the From trait and update the return type too `Result<Vec<Tag>, AppErrorType>` 

<a href="https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow/blob/master/src/db.rs">'./src/db.rs'</a>

```
pub async fn get_tags(client: &Client) -> Result<Vec<Tag>, AppErrorType> {
    let statement = client.prepare("select * from tag limit 10;").await?;
    let tags = client
        .query(&statement, &[])
        .await
        .expect("Error getting tags")
        .iter()
        .map(|row| Tag::from_row_ref(row).unwrap())
        .collect::<Vec<Tag>>();

    Ok(tags)
}
```

Let's spin our server and hit the endpoint. 

![Returned Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lf422vtpmkv6z2te1y6d.png)
The request returned a `500` status code. Instead of just panicking we are getting a status code that will help to debug.

#### But is only status code really helpful??
Not, not at all, A good error contains the cause of the error, the error status code, and a message for the client user which is human readable

#### Let's try to improve our error handling
Let's implement the AppError struct which contains our three fields' cause, error type, and message.

<a href="https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow/blob/master/src/error.rs">'./src/error.rs'</a>

```
pub struct AppError {
    pub cause: Option<String>,
    pub message: Option<String>,
    pub error_type: AppErrorType,
}
```

Just like AppErrorType, Let's implement Debug and Display trait
```
#[derive(Debug)]
pub struct AppError {
    pub cause: Option<String>,
    pub message: Option<String>,
    pub error_type: AppErrorType,
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}
```

Once we are done with `display` and `debug` trait, let's define ResponseError for AppError

```
impl ResponseError for AppError {

    fn status_code(&self) -> StatusCode {
        match self.error_type {
            AppErrorType::DbError => (StatusCode::INTERNAL_SERVER_ERROR),
            AppErrorType::NotFoundError => (StatusCode::NOT_FOUND),
        }
    }

    fn error_response(&self) -> HttpResponse {
        HttpResponse::build(self.status_code()).json(AppErrorResponse {
            error: self.message(),
        })
    }
}
```

In the above code, we have used `status_code` to match different errors and provide a status code according to it.

As soon the ResponseError is defined, We use From trait for error type conversion
```
impl From<PoolError> for AppError {
    fn from(error: PoolError) -> AppError {
        AppError {
            message: None,
            cause: Some(error.to_string()),
            error_type: AppErrorType::DbError,
        }
    }
}
impl From<Error> for AppError {
    fn from(error: Error) -> AppError {
        AppError {
            message: None,
            cause: Some(error.to_string()),
            error_type: AppErrorType::DbError,
        }
    }
}
```

Let's implement a default message for the error types.
```
impl AppError {
    // we are handling the none. the function name should match the field name
    fn message(&self) -> String {
        match &*self {
            // Error message is found then clone otherwise default message
            AppError {
                cause: _,
                message: Some(message),
                error_type: _,
            } => message.clone(),
            AppError {
                cause: _,
                message: None,
                error_type: AppErrorType::NotFoundError,
            } => "The requested item was not found".to_string(),
            _ => "An unexpected error has occurred".to_string(),
        }
    }
}
```

We are done with all the necessary changes in error.rs file. Let's start with API handlers and DB handlers

<a href="https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow/blob/master/src/api_handlers.rs">'./src/api_handlers.rs'</a>

```
pub async fn get_tags(state: web::Data<AppState>) -> Result<HttpResponse, AppError> {
    let client: Client = state.pool.get().await?;
    let result = db::get_tags(&client).await;
    result.map(|tags| HttpResponse::Ok().json(tags))
}
```

Make sure to enable the logger to get a vivid description. You can use the **actix default logger** or the **slog logger**. You can read more about slog <a href="https://dev.to/chaudharypraveen98/adding-slog-logger-to-actix-web-2332" target="_blank">here.</a>

```
async fn configure_pool(pool: Pool, log: Logger) -> Result<Client, AppError> {
    pool.get().await.map_err(|err| {
        let sublog = log.new(o!("cause"=>err.to_string()));
        crit!(sublog, "Error creating client");
        AppError::from(err)
    })
}


pub async fn get_tags(state: web::Data<AppState>) -> Result<HttpResponse, AppError> {
    let sublog = state.log.new(o!("handler" => "get_tags"));
    let client: Client = configure_pool(state.pool.clone(), sublog.clone()).await?;
    let result = db::get_tags(&client).await;
    result.map(|tags| HttpResponse::Ok().json(tags))
}
```

Let's change the Db handler file.

<a href="https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow/blob/master/src/db.rs">'./src/db.rs'</a>

```
pub async fn get_tags(client: &Client) -> Result<Vec<Tag>, AppError> {
    let statement = client.prepare("select * from tag limit 10;").await?;
    let tags = client
        .query(&statement, &[])
        .await
        .expect("Error getting tags")
        .iter()
        .map(|row| Tag::from_row_ref(row).unwrap())
        .collect::<Vec<Tag>>();

    Ok(tags)
}
```

Let's start our server and hit the API endpoint. 

**Client side error**

![Client side error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ecxn188zt544p6bocnm4.png)

**Server side error**

![Server side error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz90mn55103sxwyb10f7.png)

Wow!! We have now logs and error messages for the client user.

For Awesome logs, we have used the slog logger. If you want to follow my blog then checkout here [chaudharypraveen98 - Adding Slog Logger to Actix-web](https://dev.to/chaudharypraveen98/adding-slog-logger-to-actix-web-2332).

**Source Code**

[GitHub Source Code](https://github.com/chaudharypraveen98/actix-question-bank-stackoverflow)

References
* [Error handling and Logging with Actix Web (Rust)](https://www.youtube.com/watch?v=NHaWAWLgtAw)
* [Error Handling In Rust - A Deep Dive](https://www.lpalmieri.com/posts/error-handling-rust/)

Added Comments for your quick revision and understanding.

Feel free to ask any questions or provide suggestions. I am too learning. So will be glad to get your feedback. 

**Happy Hacking**
Rustaceans!