Overview
As part of the effort for SM-6972 - Getting issue details... STATUS , this is a proposal for implementing a unified global error handling strategy for all SaFi REST APIs.
Types of Exceptions
Any errors resulted from executing controller code will result in an exception being thrown. These exceptions will be handled based on the exception type. At a minimum, we’ll have two levels of exception handling:
domain-specific exceptions , like expected business errors or other errors specific (unique) to a domain/microservice
generic exceptions, which are common to the entire BE
Domain-specific exceptions will be handled by their respective owning microservice. The generic exceptions will be handled in a global handler, in common/utils
and used by all microservices.
Error Codes
All error codes will be documented here: Proposal: Error Codes
For each error, an appropriate HTTP error code must also be used. This document should be well-understood by the FE side, to be able to account for each error code and report it appropriately to the end users. The codes can be used as keys when implementing localization, to uniquely fetch a localized error message to be sent to the end user.
Error Response Format
A unified error response format should be used. Below are two options presented - do not use both in parallel, but instead decide on using a single one, based on the complexity of the errors being caught.
Option 1 - Simple error format, single error returned in response
This option should be enough if errors are kept simple and multi-level errors (composite errors) are not a thing.
{ "message": "Validation failed", "status": 400, "code": "VALIDATION_FAILED", "path": "/api/v1/my-endpoint?param1=val1", "timestamp": "2022-07-14T12:00:00Z", }
Option 2 - Multiple errors being returned in a single response
This option is necessary when returning errors from a Validation API or when erroring out from batch processing. In other cases, this might be overkill, so this should be used only if this more complex format is needed.
The error response will be in JSON format:
{ "message": "Validation failed", "status": 400, "code": "VALIDATION_FAILED", "path": "/api/v1/my-endpoint?param1=val1", "timestamp": "2022-07-14T12:00:00Z", "errors": [ { "code": "VALIDATION_FAILED", "message": "Validation failed for parameter param1", "parameter": "param1" }, { "code": "VALIDATION_FAILED", "message": "Validation failed for object='exampleRequestBody'. Error count: 2", "fieldErrors": [ { "code": "INVALID_SIZE", "field": "name", "message": "size must be between 10 and 2147483647", "rejectedValue": "" }, { "code": "REQUIRED_NOT_BLANK", "field": "favoriteMovie", "message": "must not be blank", "rejectedValue": null } ] }, { "code": "GENERIC_ERROR", "message": "Something went wrong", "stacktrace": "Filtered stacktrace enabled by a flag only for debugging purposes" } ] }
More info about error formats can be found here: Proposal: Global Error Handling
In order to support the error response structure above and also keep it extendable, the following class hierarchy is proposed, where new error formats can be added:
Altering the error response format based on environment
This can be done by using environment variables (or values fetched from HC Vault) and alter the format to be returned. For example, the stacktrace can be hidden from the response sent to FE, if the environment is PROD or STAGE.
Logging
All exceptions must be properly logged in the exception handler, in the same unified JSON format. For debugging purposes, be as verbose as possible. Avoid logging PII data in higher environments (can be filtered based on env variable value).
Implementation Guideline
create exception classes inside
common/utils
, corresponding to each of the generic error codes from https://safibank.atlassian.net/wiki/spaces/ITArch/pages/236093572/Proposal+Error+Codes#Generic-Error-Codes
create a
GlobalExceptionHandler
insidecommon/utils
structure can be similar to https://github.com/SafiBank/SaFiMono/blob/main/services/loan-manager/src/main/kotlin/ph/safibank/loanmanager/exception/ExceptionHandlers.kt
must cover all errors described in https://safibank.atlassian.net/wiki/spaces/ITArch/pages/236093572/Proposal+Error+Codes#Generic-Error-Codes
all services must use this exception handler, therefore ensuring a common structure for the generic error types and their responses to FE
create
GenericErrorResponseProcessor
insidecommon/utils
, similar to https://github.com/SafiBank/SaFiMono/blob/main/services/loan-manager/src/main/kotlin/ph/safibank/loanmanager/exception/DefaultErrorResponseProcessor.ktunless the consuming service has a more specific implementation for any particular reason, this one should support the error response hierarchy described above
each service can add domain-specific errors by creating their own domain-specific exception handlers
each new error codes must be added to https://safibank.atlassian.net/wiki/spaces/ITArch/pages/236093572/Proposal+Error+Codes#Domain-specific-Error-Codes
Attachments:
plantuml_1671443280637.svg (image/svg+xml)
plantuml_1671443280637.png (image/png)
plantuml_1671443280637.svg (image/svg+xml)
plantuml_1671443280637 (text/plain)
plantuml_1671443280637.png (image/png)
plantuml_1671443280637 (text/plain)
plantuml_1671443280637.svg (image/svg+xml)
plantuml_1671443280637.png (image/png)
plantuml_1671443280637.svg (image/svg+xml)
plantuml_1671443280637 (text/plain)
plantuml_1671443280637.png (image/png)
plantuml_1671443280637 (text/plain)
plantuml_1671443280637.svg (image/svg+xml)
plantuml_1671443280637.png (image/png)