Errors management at relay
Relay error handler is a package used by Node.js micro services to return consistent error nodes from the API response.
- Types of errors/exceptions
- How do we handle them?
- Shape of error objects
- Usage
- Defining codes for exceptions
- Change log
Before getting started, let's distinguish between the types of error an application can have and why having a consistent output is important.
Validation errors are the most straight forward one's. The service validates the user input and returns one or more errors, when the incoming data is not valid.
We make use of indicative for running data validations. Indicative offers an API to define error formatters, can be used to re-shape the errors array.
This module (relayin/error-handler)
configures a custom error formatter with Indicative to have a consistent validation error nodes.
The flow exceptions occurs, when a backend service peforms inline checks before taking certain actions. Notice we make use keyword exception
and not error
. Their is a slight difference between error and exception. For example:
A client makes a request to one of our service and we notice that their account is invalid. At first place, they shouldn't be able to make this request with an invalid account.
Flow exceptions occurs when a request reaches a guard intentionally added for extra security.
As the name says, unexpected exceptions are unanticipated exceptions that must be handled gracefully and logged to the system for further inspection.
At relay, we make sure to uniquely identify an error or exception with a numeric code like 10001
. The numeric codes are inspired from twilio and has following benefits.
- You don't have to think hard for unique names. Simply increment the error and write documentation for it.
- It's easier to have unique code per micro service. Assign a numeric bucket like 1000 to auth and 2000 to directory and so on.
The numeric codes not only makes it easier to identify the error. It gives freedom to the clients to display error messages in the user language by mapping them against the code.
The shape of error objects is driven by jsonapi spec and looks as follows.
{
code: 10001,
title: 'Generic error message title in english',
source: {
pointer: 'data/field/reference',
},
}
The source
object will be removed when error is not related to a field.
{
code: 10002,
title: 'Inactive account',
}
Finally let's jump on the usage of this module. It automatically configures an exception handler for AdonisJs and a error formatter for Indicative.
Install it from npm as follows:
npm i @relayin/error-handler
and then use it as a service provider inside start/app.ts
file.
export const providers = [
'@relayin/error-handler/build/providers/ErrorHandlerProvider'
]
Finally, within the app exception handler you can use Relay/ExceptionManager
to convert Exceptions to api response objects.
const ExceptionManager = use<Relay.ExceptionManager>('Relay/ExceptionManager')
class ExceptionHandler {
async handle (error, { response }: AdonisJs.Context) {
const { status, errors } = ExceptionManager.toResponse(error)
response.status(status).send({ errors })
}
}
That's all from the usage point of view. The validation errors gets a unique set of error codes, which are shared by all micro service and you won't have to do much on that front.
For exceptions, you need to create a config file config/errorCodes.ts
and define codes for them.
export const exceptionCodes = {
ERR_GATEWAY_FAILURE: 20001,
ERR_MISSING_OTP: 20001,
ERR_INVALID_OTP_LENGTH: 20001,
ERR_MISSING_MOBILE_NUMBER: 20001,
ERR_UNKNOWN_GATEWAY_FAILURE: 20001,
}
export const errorCodes = {
20001: {
message: 'Unable to send SMS. Try again after some time'
},
}
export const codesBucket = 20000
Following is the explaination for the config file.
- exceptionCodes: Exception codes are logged to the logging service. They are used for debugging and should never be exposed to the client.
- errorCodes: The
errorCodes
is the list of codes and their generic messages, you want to return to the client. It is possible that for multiple exceptions you want to return a generic code to the client, sicne they don't take if sending SMS failed coz ofE_GATEWAY_FAILURE
orE_INVALID_API_KEY
. - codesBucket: The bucket ensures that you always use numeric codes within the range of your bucket and avoid conflicts with micro services.
The changelog can be found in the CHANGELOG.md file.