Error Handling on Microservices architecture

Microservices with Node JS and React

The project contains a submodule, clone with these parameters:

git clone –recursive https://github.com/josecho/MicroServicesNatsStreamingBlog.git

What is the error handling technique?

First of all, say that error handling refers to the response and recovery procedures from error conditions present in a software application. In other words, it is the process comprised of anticipation, detection, and resolution of application errors, programming errors, or communication errors.

Therefore, an Error Handling Middleware has been created in this project. The error-handling middleware allows us to separate our error logic and send responses accordingly. Error handling is important because it makes it easier for the end users of your code to use it correctly. Another important issue is that it makes your code easier to maintain.

On the other hand, in a microservices architecture, we have to make sure that the error comes from our services always satisfying that same structure. In this project all error responses that we send out from any server should have this structure:

Common error responses

Common Response Structure

Each service can be implemented with a different programming language and these languages create an error response with its own structure, particular to each service. Consequently, this would force the React application to have the necessary knowledge of how to handle a lot of errors.


Complexity arround errors


In order to avoid this, a common error response structure is used in this project so that the React application only needs to know how to parse 1 type of error.


same structure

Difficulty in Error Handling

  • We must have a consistently structured response from all servers, no matter what went wrong

    Write an error handling middleware to process erros, give them a consistent structure, and send back to the browser.

  • A billions things can go wrong, not just validation of inputs to a request handler. Each of these need to be handle consistently

    Make sure we capture all possible errors using Express’s error handling mechanism (call the ‘next’ function)

Error Object

We have no idea where something is going to go wrong inside our application.

Something wrong inside of our application

To handle these situations, Error Handling Middleware was created in the application. In this app, we just want to have one single identical structure for all possible errors across all of our different services. Therefore, we have taken the first steps in this way.

res.status(400).send({
message: 'Something went wrong',
})

After connecting the middleware to our express app.

app.use(errorHandler);

So in order to make sure we have a very consistently structured response no matter what, instead of manually writing error handling code like the one that existed in the signup.ts file:

return res.status(400).send(errors.array());

Every time something goes wrong, we’re going to throw an error.

throw new Error('Invalid email or password');
throw new Error('Error connecting to database...');

All these changes can be seen in the code repository indicated by the link above. On the other hand, the image above it says that we need to somehow attach a custom error message to the object error. That is done in this step.

After what we have seen so far we have taken a series of steps.

But we are going to focus on the last steps taken, where I think there is something important that we must learn.

Right now we are throwing our RequestValidationError and the DatabaseConnectionError into that Error Handling Middleware.
And we’ve encoded inside there some very intricate knowledge of exactly how to extract information from every kind of error that exists inside of our application.

Errors

Let’s imagine some scenario down the line where maybe we have a lot of different things that can go wrong inside of our application and we create a custom error for everything that can go wrong. And so we end up with all these different custom errors as shown in the following image:

Erros

Consequently, our Error Handling Middleware is going to grow to a giant size. We will have to encode some logic inside there to understand every possible error and understand how to extract information from them to send down to a user making sure that they all follow this same kind of common response structure. If we continue down the same path, we’re going to end up with our Error Handling Middleware growing to be extremely, extremely large.

SerializeError method and Status Code propertie

So how can we fix this?

Well, we’re going to kind of inverse or take the opposite of this relationship.

Logic in Errors

As a new approach, Error Handler Middleware is just going to make sure that it understands the errors that come to it and accesses the method built on them called serializeError. This method returns an array containing elements of type Common Error Structure. At the same time, it can also refer to the Error Status Code to understand what status code to use inside the response as well.

Now, if the above-mentioned scenario is repeated in which many errors occur in the application:

serializeError method

We can create as many Error custom classes as we want. Each one can have its own special implementation. The Error Handling Middleware is not going to grow in complexity because no matter what the error is, we’re always going to refer to the same kind of serializedError function and the same statusCode property. Let’s look at this step in the code.

Verifying Our Custom Erros

At present, there is absolutely nothing inside our TypeScript code to verify and make sure that the serializeError function is put together correctly. It would be really nice if we had some kind of check inside of our code. Something to make sure that serializeError is always going to return this kind of structure.

Check Errors

We need to implement some code to make sure that all of the different services that we’re going to create over time are always going to return the exact same structure of error. So again, it’d just be really great to make sure that we had something inside the TypeScript world to double-check that.

Check Errors

There are two possible approaches.

Option #1

TypeScript interface creates a contract on RequestValidationError and DataBaseConnectionError to ensure that these custom errors correctly implement the serializeError method and statusCode property.

CustomError Interface

TypeScript Interface

Option #2

We are going to create a new abstract class called CustomError. The abstract class is going to serve the exact same purpose as the interface that we just looked at.

Abstract class

The nice thing about an abstract class is that when we translate this over to JavaScript to actually execute, we actually end up with a class definition. When we translate Typescript interfaces to JavaScript, all interfaces fall away. They don’t actually exist in the world of JavaScript, but abstract classes do.

Final Solution

The TypeScript compiler does not convert the interface to JavaScript. It uses an interface for type-checking. This is also known as “duck typing” or “structural subtyping”. If we continue with the same pattern we’ve followed up to this point, we’ll eventually have to have a bunch of different if statements for all the different types of errors.

ton of if statements

Rather than defining an interface and having our classes implement it, we’re going to instead define an abstract class called CustomError, and we’re going to have RequestValidation and DatabaseConnectionError extend that abstract class. As a result in Error Handler Middleware, we will only have one if statement for all the different error types.

abstract class solution

In conclusion, we have to make sure that the error comes from our services always satisfying that same structure and it is by doing things like this here, literally exactly this, that we will make sure that all the different areas that we ever create within our application have that identical structure.

Maybe it’s a good time to see how exceptions are handled in Spring Boot Microservices and compare it with Node Microservices.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *