Explaining Error Handling

Objectives
After completing this lesson, you will be able to:

After completing this lesson, you will be able to:

  • Explain error handling

Explain Error Handling

Error Types

Proper error handling is crucial for today's business applications. Before going into more detail, it is necessary to distinguish between two types of errors:

  • Programmer errors.

    These occur as a result of programming errors (for example, 'foo' cannot be read by undefined). They must be corrected.

  • Operational errors

    These occur during runtime (for example, when sending a request to a faulty remote system). They must be corrected.

    Guidelines

    The key takeaways for programming errors are:

    • Fail loudly: Do not hide errors and continue silently. Make sure to log unexpected errors correctly. Don't catch errors that you can't handle.
    • Don't develop in a defensive fashion. Focus on your business logic and only handle errors when you know they will occur. Use try/catch blocks only when necessary.

    Never try to catch and handle unexpected errors, rejections of promises, and so on. If it is unexpected, you cannot handle it correctly. If you could, it would be expected (and should already be handled). Even if your apps should be stateless, you can never be 100% sure that a shared resource was not affected by the unexpected error. Therefore, you should never allow an app to continue running after such an event, especially for multi-tenant apps where there is a risk of information disclosure.

    Following this will make your code shorter, clearer and simpler.

Never hide the causes of errors.

When an error occurs, it should be possible to know the root cause. The SAP Cloud Application Programming Model SDK for Node.js also throws exceptions, for example when a CRUD operation violates the foreign key constraints. In this case, the framework throws the exception UNIQUE_CONSTRAINT_VIOLATION. The problem in this case is that the end user will only see a more or less cryptic error message:

It is therefore useful to provide a meaningful error message.

For this purpose, you can register an error handler in your service implementation:

Example:

Code snippet
// Imports
const cds = require("@sap/cds");

/**
 * The service implementation with all service handlers
 */
module.exports = cds.service.impl(async function () {
 /**
 * Custom error handler
 *
 * throw a new error with: throw new Error('something bad happened');
 *
 **/
 this.on("error", (err, req) => {
 switch (err.message) {
 case "UNIQUE_CONSTRAINT_VIOLATION":
 err.message = "The entry already exists.";
 break;

 default:
 err.message =
 "An error occured. Please retry. Technical error message: " +
 err.message;
 break;
 }
 });
});
Expand

This handler now steps in whenever this exception gets triggered and overrides it with an alternative error message:

Raising and catching exceptions

You will certainly add your implementations to your services. It is very likely, that you want to interrupt some operations before something crashes. In this case, you can throw a Node.js exception. for error handling.

Request response

You can also use the req.error() method to collect messages or errors and return them to the caller in the request-response.

Code snippet
this.on("submitOrder", async (req) => {
 const { book, amount } = req.data;
 let { stock } = await db.read(Books, book, (b) => b.stock);
 if (stock >= amount) {
 await db.update(Books, book).with({ stock: (stock -= amount) });
 await this.emit("OrderedBook", { book, amount, buyer: req.user.id });
 return req.reply({ stock }); // <-- Normal reply
 } else {
 // Reply with error code 409 and a custom error message
 return req.error(409, `${amount} exceeds stock for book #${book}`);
 }
});
Expand

Log in to track your progress & complete quizzes