Skip to content

Commit

Permalink
refactor: error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
sheroz committed Feb 15, 2025
1 parent 9d35f59 commit e3ad236
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 21 deletions.
64 changes: 62 additions & 2 deletions docs/api-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@

## Errors

The possible error codes and description.
### The possible error codes and description

- `authentication_wrong_credentials`: The provided credentials are incorrect.
- `authentication_missing_credentials`: Required authentication credentials are missing.
Expand All @@ -334,4 +334,64 @@ The possible error codes and description.
- `database_error`: There was an error with the database operation.
- `redis_error`: There was an error with the Redis operation.

## END
### The possible error kinds and description

- `authentication_error`: An error occurred during the authentication process.
- `resource_not_found`: The requested resource could not be found.
- `validation_error`: There was a validation error with the provided data.
- `database_error`: An error occurred with the database operation.
- `redis_error`: An error occurred with the Redis operation.

### API error response samples

```json
{
"status": 404,
"errors": [
{
"code": "user_not_found",
"kind": "resource_not_found",
"message": "user not found: 12345",
"description": "user with the ID '12345' does not exist in our records",
"detail": { "user_id": "12345" },
"reason": "must be an existing user",
"instance": "/api/v1/users/12345",
"trace_id": "3d2b4f2d00694354a00522fe3bb86158",
"timestamp": "2024-01-19T16:58:34.123+0000",
"help": "please check if the user ID is correct or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information",
"doc_url": "https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md"
}
]
}
```

```json
{
"status": 422,
"errors": [
{
"code": "transfer_insufficient_funds",
"kind": "validation_error",
"message": "source account does not have sufficient funds for the transfer",
"reason": "source account balance must be sufficient to cover the transfer amount",
"instance": "/api/v1/transactions/transfer",
"trace_id": "fbb9fdf5394d4abe8e42b49c3246310b",
"timestamp": "2024-01-19T16:58:35.225+0000",
"help": "please check the source account balance or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information",
"doc_url": "https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md"
},
{
"code": "transfer_destination_account_not_found",
"kind": "validation_error",
"message": "destination account not found: d424cfe9-c042-41db-9a8e-8da5715fea10",
"detail": { "destination_account_id": "d424cfe9-c042-41db-9a8e-8da5715fea10" },
"reason": "must be an existing account",
"instance": "/api/v1/transactions/transfer",
"trace_id": "8a250eaa650943b085934771fb35ba54",
"timestamp": "2024-01-19T16:59:03.124+0000",
"help": "please check if the destination account ID is correct or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information.",
"doc_url": "https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md"
},
]
}
```
47 changes: 34 additions & 13 deletions src/api/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,45 @@ pub const API_DOCUMENT_URL: &str =
// ]
// }
//
// ---
//
// {
// "status": 422,
// "errors": [
// {
// "code": "invalid_email",
// "code": "transfer_insufficient_funds",
// "kind": "validation_error",
// "message": "user email is not valid",
// "description": "validation error in your request",
// "detail": { "email": "xyz@12345" },
// "reason": "must be a valid email address",
// "instance": "/api/v1/users/12345",
// "message": "source account does not have sufficient funds for the transfer",
// "reason": "source account balance must be sufficient to cover the transfer amount",
// "instance": "/api/v1/transactions/transfer",
// "trace_id": "fbb9fdf5394d4abe8e42b49c3246310b",
// "timestamp": "2024-01-19T16:58:35.225+0000",
// "help": "please check if the user email is correct or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information",
// "help": "please check the source account balance or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information",
// "doc_url": "https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md"
// },
// {
// "code": "transfer_destination_account_not_found",
// "kind": "validation_error",
// "message": "destination account not found: d424cfe9-c042-41db-9a8e-8da5715fea10",
// "detail": { "destination_account_id": "d424cfe9-c042-41db-9a8e-8da5715fea10" },
// "reason": "must be an existing account",
// "instance": "/api/v1/transactions/transfer",
// "trace_id": "8a250eaa650943b085934771fb35ba54",
// "timestamp": "2024-01-19T16:59:03.124+0000",
// "help": "please check if the destination account ID is correct or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information.",
// "doc_url": "https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md"
// },
// ]
// }
//
// ---
//
// (Users endpoint can be extended to handle these validations)
//
// {
// "status": 422,
// "errors": [
// {
// "code": "invalid_birthdate",
// "kind": "validation_error",
// "message": "user birthdate is not correct",
Expand All @@ -58,13 +80,13 @@ pub const API_DOCUMENT_URL: &str =
// "instance": "/api/v1/users/12345",
// "trace_id": "8a250eaa650943b085934771fb35ba54",
// "timestamp": "2024-01-19T16:59:03.124+0000",
// "help": "please check if the user birthdate is correct or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information."
// "help": "please check if the user birthdate is correct or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information.",
// "doc_url": "https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md"
// },
// {
// "code": "invalid_role",
// "kind": "validation_error",
// "message": "user birthdate is not correct",
// "message": "role not valid",
// "description": "validation error in your request",
// "detail": { role: "superadmin" },
// "reason": "allowed roles: ['customer', 'guest']",
Expand All @@ -74,8 +96,7 @@ pub const API_DOCUMENT_URL: &str =
// "help": "please check if the user role is correct or refer to our documentation at https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md#errors for more information",
// "doc_url": "https://github.com/sheroz/axum-rest-api-sample/blob/main/docs/api-docs.md"
// },
// ]
// }

#[derive(Debug, Serialize, Deserialize)]
pub struct APIError {
pub status: u16,
Expand Down Expand Up @@ -216,8 +237,8 @@ impl APIErrorEntry {
self
}

pub fn doc_url(mut self, doc_url: &str) -> Self {
self.doc_url = Some(doc_url.to_owned());
pub fn doc_url(mut self) -> Self {
self.doc_url = Some(API_DOCUMENT_URL.to_owned());
self
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/api/handlers/transaction_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,23 +128,24 @@ impl From<TransferValidationError> for APIErrorEntry {
TransferValidationError::InsufficientFunds => error
.code(APIErrorCode::TransferInsufficientFunds)
.kind(APIErrorKind::ValidationError)
.description(
"there are insufficient funds in the source account for the transfer".into(),
)
.reason("source account balance must be sufficient to cover the transfer amount")
.trace_id(),
TransferValidationError::SourceAccountNotFound(source_account_id) => error
.code(APIErrorCode::TransferSourceAccountNotFound)
.kind(APIErrorKind::ValidationError)
.detail(serde_json::json!({"source_account_id": source_account_id}))
.reason("must be an existing account")
.trace_id(),
TransferValidationError::DestinationAccountNotFound(destination_account_id) => error
.code(APIErrorCode::TransferDestinationAccountNotFound)
.kind(APIErrorKind::ValidationError)
.detail(serde_json::json!({"destination_account_id": destination_account_id}))
.reason("must be an existing account")
.trace_id(),
TransferValidationError::AccountsAreSame => error
.code(APIErrorCode::TransferAccountsAreSame)
.kind(APIErrorKind::ValidationError)
.reason("source and destination accounts must be different")
.trace_id(),
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/api/handlers/user_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl From<UserError> for APIErrorEntry {
.instance(&format!("/api/v1/users/{}", user_id))
.trace_id()
.help(&format!("please check if the user ID is correct or refer to our documentation at {}#errors for more information", API_DOCUMENT_URL))
.doc_url(API_DOCUMENT_URL)
.doc_url()
}
}
}
4 changes: 2 additions & 2 deletions src/application/service/transaction_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ pub enum TransferError {

#[derive(Debug, Error)]
pub enum TransferValidationError {
#[error("insufficient funds")]
#[error("source account does not have sufficient funds for the transfer")]
InsufficientFunds,
#[error("source account not found: {0}")]
SourceAccountNotFound(Uuid),
#[error("destination account not found: {0}")]
DestinationAccountNotFound(Uuid),
#[error("source and destination accounts must be different")]
#[error("source and destination accounts are the same")]
AccountsAreSame,
}

0 comments on commit e3ad236

Please sign in to comment.