Error handling examples

IBM Financial Services Workbench offers the handling of these main types of errors:

  • Business errors which can occur if business conditions are not met but are expected to happen

  • Validation errors

  • General errors (all others)

Error handling logic can be implemented in several places:

  • Domain services or commands within their handleError() method

  • API operation class within its handleError() method

  • API ErrorMiddleware class within its handleError() method

Domain error handling

Inside a domain namespace a business error can be thrown whenever a domain service or a command can/should not be executed due to conditions that are not fulfilled, e.g., minimumAmountOrdered should be 5 but customer inputs 4.

You can create as many business errors as needed.

Within the implementation file of a domain service or a command you can paste the following code to throw a business error:

throw this.factory.error.nsacrnm.MyBusinessError();

where nsacronym must get replaced with the acronym of the domain namespace in which you created the business error and MyBusinessError gets replaced with the actual name of the business error you want to throw (and have defined previously in the design model).

Every error thrown in an implementation file will be handled by the handleError method of the implementation that receives the error (if not yet handled otherwise). In the following example the domain namespace is called orderm and the business error thrown in the domain service is called NoOrdersAvailable. So at the end of the implementation file of the API operation the code would look similar to this:

...

/**
   * This function is automatically called when an error occurs within the execution flow of an operation
   * @param error Operation execution error that occurred.
   */
  public async handleError(error: Error): Promise<void> {
    const log = this.util.log;

    // First check if the error received is a business error
    if (this.isInstanceOf.error.BusinessError(error)) {

      // Now check if the received error is a NoOrdersAvailable error
      if (this.isInstanceOf.businessError.orderm.NoOrdersAvailable(error)) {

        // do something special because this business error was thrown
      }
    }

If you need to check for other error types (e.g., validation errors, etc.) please see TypeScript Solution Framework (SDK) - isInstanceOf checks for further details.

Error Middleware

ErrorMiddleware provides a centralized place for common error handling logic independent of API operation context. Path of this file will be src-impl/api/API-namespace-prefix/middleware/ErrorMiddleware. Within ErrorMiddleware handleError() general error handling logic can be implemented by checking the passed operation execution error that occurred and setting response accordingly.

Note: ErrorMiddleware handleError() method will be executed only if the API Operation's handleError() method is not implemented / did not set a proper response.

If ErrorMiddleware handleError() is not implemented / did not set a proper API operation response, then the original error will be returned as API operation response with a 400 / 500 status code depending on error type (system error / business error / validation error). An API operation response set within this class via (this.response) might end up being an undocumented API response for the executed operation. Please make sure the response status code and schema you set have been modelled and added to all the API namespace operations.

The class hierarchy of errors consists of GeneralError, ValidationError and BusinessError, where ValidationError and BusinessError extend GeneralError. See below example:

/**
* This class is responsible for providing a centralized place for common
* error handling logic independent from API operation context,
* API operation response set within this class via (this.response)
* might end up being not documented API response for the executed operation.
*/
export default class extends middleware.apitest_ErrorMiddleware {

  // This script gets executed if any error happens throughout the operation execution
  public async handleError(error: Error): Promise<void> {

    // Handle errors that may occur throughout operation execution
      if (this.isInstanceOf.error.BusinessError(error)) {

            this.response = new namespace_SomeOperationResponse();
            this.response.status = 400;
            this.response.body = this.factory.schema.v1.BadRequestError();
            this.response.body.message = 'BadRequestError';

        } else {

            this.response = new namespace_AnotherOperationResponse();
            this.response.status = 500;
            this.response.body = this.factory.schema.v1.SystemError();
            this.response.body.message = 'SystemError';
      }

  }
}

Operation error handling

Operation handleError() method can be used to implement error handling logic for the errors that might occur within an API operation's execution.

The handleError() method is executed automatically if an error occurs, it gets the error that occurred as an argument. The method will have access to operation modelled responses. The passed error can then be checked using isInstanceOf utility and the operation response can be set accordingly.

Note: If handleError() method on operation level is not implemented / did not set a proper response, then ErrorMiddleware handleError() will be called.

See below example (ehns is the name of the API namespace):

export default class extends operations.ehns_getPizza {

  public async execute(): Promise<void> {
    const log = this.util.log;
    log.debug('ehns_getPizza.execute()');
    // Execute some logic below that might result in an error
  }

  /**
  * This function is automatically called when an error occurs within the execution flow
  * of this operation ehns_getPizza @param error Operation execution error that occurred
  */
  public async handleError(error: Error): Promise<void> {
    const log = this.util.log;
    log.info('ehns_getPizza.`handleError()`');
    // Add Error handling logic below and set this.response that will be returned as operation ehns_getPizza response

    if (this.isInstanceOf.error.GeneralError(error)) {
      this.response.status = 500;
      this.response.body = this.factory.schema.ehns.GeneralErrorSchema();
      this.response.body.errorCode = 'GE101';
      this.response.body.errorMessage = 'General Error occurred, original Error message: '
      + error.message;
    }

    if (this.isInstanceOf.error.ValidationError(error)) {
      this.response.status = 400;
      this.response.body = this.factory.schema.ehns.GeneralErrorSchema();
      this.response.body.errorCode = 'VE101';
      this.response.body.errorMessage = 'Validation Error occurred, original Error message: '
      + error.message;
    }

    if (this.isInstanceOf.error.BusinessError(error)) {
      this.response.status = 400;
      this.response.body = this.factory.schema.ehns.GeneralErrorSchema();
      this.response.body.errorCode = 'BE101';
      this.response.body.errorMessage = 'business error occurred, original Error message: '
      + error.errorMessage;
    }
  }
}