It's great that everything was a bed of roses while our applications were running, but sometimes incidents happen or even something unexpected.
The process of Exception handling is what allows us to handle these types of events. It's something that allows us to avoid catastrophes and turn around unexpected situations. Basically, in the midst of chaos, it gives us control. Errors can be caused by various reasons, such as the following:
To better understand these concepts, the material in this chapter is divided as follows:
By the end of this chapter, we should be able to understand the usefulness of Exceptions, in what context we should use them, and how to throw and handle them.
Check out the following video to see the code in action: https://bit.ly/3FFpQBu.
The OutSystems platform, out of the box, already provides a system to handle Exceptions. However, we can raise them in our code wherever we see fit. With this, we can gain greater control over unexpected events and process them in a more user-friendly and understandable way. Once again, it all comes down to being able to take a neat leap.
Something to bear in mind whenever we raise an Exception is that it will be logged and visible on the Service Center console, which allows us to subsequently have a more careful and in-depth analysis of anomalies and a more precise intervention.
We can perform raise Exceptions on our Server Actions, Service Actions, and Client Actions (these last two will be seen in Chapter 10, Client-Side Logic).
Whenever we foresee a possibility of unexpected behavior, through the decision elements seen in Chapter 6, Server-Side Logic (If and Switch), we can trigger an Exception of the following types:
As a rule, if we want the log of this event to be scaled to the module level in which it occurs, we must add the respective handler in parallel to the "happy path" flow of the respective Server Action (as we can see in Figure 7.1).
We can also create Exceptions as needed by our applications, since many times, the ones that already exist by default do not cover all the scenarios we intend. Our custom Exceptions are called user Exceptions and we can raise them in our flows whenever necessary. These Exceptions are considered child nodes of general user Exceptions and we can create them in the Exceptions section in the Service Studio Logic tab. Just right-click on User Exceptions and select the Add User Exception option. The only property we have to define is its name (we must be careful to choose a self-explanatory name). From that moment on, that Exception can be invoked in our flow and we have the handler available to use in our Actions.
What can we do to treat our Exceptions? How do we process the flow of our handlers? That's what we'll see in the next section!
Whenever an unexpected event of an Action occurs and it is redirected to a handler, it can be customized and have its own logic in order to give us flexibility in handling errors and allow a reduction of impact on the end user.
In Exception handlers, we can choose between aborting the transaction or not (rollback of all inserts, updates, or deletes made to the database during the flow that has not been committed) and whether we want to log the error or not, as shown in Figure 7.2:
Sometimes, in scenarios where we want to scale the error to module or consumer Actions, we define an output variable for this purpose, being of the type of a structure with two attributes:
In the flows of Exception handlers, we can manipulate these variables and any others within the scope of the Action. There are cases of high complexity where the logic used in Exceptions handlers allows us to carry out sets of operations that keep all types of information related to the error, thus facilitating its tracking and allowing faster analysis, as shown in Figure 7.3:
We can make raise Exceptions without having the Exception handler defined in our Server Action, so that the same Exception bubbles up to the next modular level until it finds its handler. In cases where you reach the highest level without finding the Exception handler, it is processed by the global Exception.
Tip
If you are curious, you can see the official documentation on handle Exceptions at https://success.outsystems.com/Documentation/11/Developing_an_Application/Implement_Application_Logic/Handle_Exceptions and https://success.outsystems.com/Documentation/11/Developing_an_Application/Implement_Application_Logic/Handle_Exceptions/Exception_Handling_Mechanism.
What is a global Exception and how does it work? What is it for? As we'll see in the next section, it can be seen as our lifeline.
A global Exception is intended to log all events arising from Exceptions not handled during the operation of applications.
In cases where there is no specific Exception handler to handle something that occurs at a certain level, it will bubble up through the functionality's call stack until it finds the handler it needs. If you don't find it until you reach the last level (usually the frontend module of the application), the global Exception of that module does the job.
These handlers are defined in a frontend flow and are then associated with the module in the Global Exception Handler attribute, as shown in Figure 7.4:
With this, we realize that the global Exception handler is our last resort as it allows us to handle errors and unexpected events, and it prevents our application from being in limbo in case of a crash or accident. This way, we can understand what is wrong or what happened with the application.
Now, in the next section, in order to consolidate these topics, we will do an exercise (following the previous exercises related to the Help Desk application) where we will raise and handle an Exception.
In this exercise, we will implement a raise Exception and its handler to handle calls made to the Ticket_CreateOrUpdate Server Action by users who are not registered (who did not authenticate themselves through a set of credentials on a login page):
With these steps, we guarantee that if someone unregistered uses the functionality, we log the event associated with the module in question, and, in addition, we expose the error in a user-friendly manner to consumers so that they can better understand the behaviors obtained in the situation that triggered the exception handler.
In this chapter, we understood that the platform provides artifacts that make it easier for us to process unexpected events in our applications. Furthermore, the way they relate and can be applied provides us with much greater control over what happens to these events, thus ensuring a much more understandable behavior for users (that is, better management of expectations).
With this, we realize that we can trigger our own Exceptions, we can use the Exception handlers in the most convenient way, the platform automatically bubbling up the events that do not find a handler to the Exception that was thrown, and, as a last resort, we have our safeguard: a global Exception handler.
All these features help a lot in two extremely important points of our applications: tracking errors and trends and making our applications more robust in case of errors.
All of this is an incredibly simple and fast way, without having to lose focus on business concepts, to write countless lines of code.
Do you know where we will be able to enjoy the results of applying these features? What we'll see in the next chapter: the frontend!
Let's start by looking at premade patterns, widgets, and templates that save us a lot of development time!
We will also learn how to scaffold screens (another superb accelerator in Service Studio) and how we can easily customize our CSS and insert our JavaScript! Incredible! Come on!