This document is intended to be a brief guide to error handling in a typical Threekit configurator implementation to help catch errors, report errors, and mitigate the impact of errors.
Note: This is not intended to discuss internal logic errors that can/should be tested and caught during development. This is intended for unexpected runtime errors that could have potentially significant consequences in a production environment.
Where reasonable, attempts should be made to catch errors. This typically means not assuming responses from calls to external apis (whether local JS or remote REST apis) will be successful and contain the desired/expected data structure and information. There are several scenarios where errors can arise and are handled in different ways. These are outlined below.
Types of Errors
Any apis that return promises may return rejected promises, indicating that something went wrong with the call. Thus when handling a promise response from an external api, you should catch errors by explicitly handling the rejection via the “catch” callback, ex:
Note that if using "await", a rejected promise will throw an error, just as a "throw new Error()" statement would. If you want to use await, you would need to wrap the call in a try/catch block to catch and handle errors.
Sometimes your application will need to make requests to a backend api via HTTP request. For example, your frontend may make requests to your backend application server, or your application may make requests to various Threekit server-side apis such as the catalog, configuration saving/loading, renders, etc. It is particularly important with network calls to not assume success, since there can be errors in several aspects of the process:
- the request payload/headers
- transient network issues
- outage of the api service
- unexpected change in the api service resulting in failure to handle the request in an expected manner
Http requests return a response, which has a response code as well as any content data associated with the response. Before processing the response, you generally want to first check the response code to ensure it does not indicate a failure (typically, 2XX-3XX is a success, 4XX-5XX is failure). If the code indicates success, the next step is to parse the incoming data (if there is expected/required response data) and ensure it has the appropriate structure/data needed for the application.
In addition to explicit failure/errors in the request, note that both network and api service issues can also result in a request pending indefinitely or for a much longer time than acceptable. Many http libraries have timeout defaults, but allow you to specify your own limits. Depending on the use-case of the request, it may be prudent to set reasonable limits and implement appropriate logic for when the request times out.
In general you shouldn’t need to account for this, as wrapping every single external JS call in a try/catch would be overkill and unnecessarily clutter/complicate your code.
However, there are times when you can not be fully confident in the ability of an api function being called to handle the input. Either that or the function in question is intentionally designed to handle certain inputs by throwing an error. In these cases, it makes sense to guard the call with a try/catch block and handle failure appropriately.
The previous section discusses common scenarios where an error can occur and how to catch it. This leaves the question of how to handle the error once we know we have encountered one. Typically, this is driven by the use case in which the error is encountered, and what the end-user’s impact and expectations are. The following are a few guiding principles recommended for a best user experience:
- Minimize error propagation - Errors that are unhandled or simply caught and not handled properly can cause an accumulation of problems in the configurator and anything it communicates with. This can cause the original error to have far more serious of an impact than it should have. For example, if adding a configuration to a shopping cart invokes a thumbnail generation, poor handling of failure of that thumbnail generation could result in the user being unable to even add the configured product to the cart at all, affecting the client’s business.
- Implement reasonable fallback logic - For some errors, it may be possible to fallback to some alternative/default behaviour so that the user’s experience is minimally affected. For example, rather than breaking the configurator, the action that encountered the error could result in no side effects at all. The user may wonder why the action didn’t work, but at least the rest of the configurator remains functional.
- Error visibility to end-user - Particularly in cases where there are no reasonable fallback behaviour and the chance for user impact or confusion is high, it may be warranted to display a message to the user in the UI indicating the issue in a user-friendly way, as well as action they could take to fix/avoid it, if applicable.