Home > Enterprise >  Error Handling in nested API Calls with express and nodeJS
Error Handling in nested API Calls with express and nodeJS

Time:09-22

I have 3 microservices with an API interface with the goal to create a new user in my system.

  1. service1 --> is the main service and will orchestrate the request along the other services
  2. service2 --> will create the user in Firebase
  3. service3 --> will create the user in the Data Base

The frontEnd will call service1 and it will call first the service2 and then the service3. With the result, service1 will respond the frontEnd.

If each service respond 200-OK everything works fine, the problem is the error handling.

When service2 respond 401- "User already exist in system" to service1, I only can retrieve from error "message: Request failed with status code 401" and not the text "User already exist in system" sent by service2.

How can I do for service1 will pass through the error code and error message sent by service2? 401- User already exist in system

service1.js

app.post('/createAdminAgent', async (req, res) => {   
    let body = req.body;
    try {           
        const response_createAgentFirebase = await axios.post(service2URL   '/createAgent', req.body); //the user exist and will respond 401
        const response_createAgentBDD = await axios.post(service3URL   '/createEnterpriseAndAdminAgent', req.body);
        res.status(200).send(response_createAgentBDD.data);
    } catch (error) {           
        res.status(400).send(error); // how can I get here the text "User already exist in system" and the status code 401 sent by service2
          

    }                   
                    
});

service2.js

app.post("/createAgent", async (req, res) => {  
    createAgentOnFirebase(req.body, async function(err, firebaseResult) {       
        if (err == null){
            res.status(200).send(firebaseResult);   
        }else{
            res.status(401).send({
                                 status: 401,
                                 error: 'User already exist in system'
                                 })
        }
    })
})

CodePudding user response:

To answer the question that you have in the comment of the try { } catch() { } block of your main service route handler:

"how can I get here the text "User already exist in system" and the status code 401 sent by service2"

You should pass any errors to a custom error handling middleware function (which you can define yourself) and handle errors within those functions, rather than in the route handlers themselves. Trying to handle errors within the route callbacks themselves gets messy, and often leads to duplicative code.

Triggering Error Handlers

To trigger the next express error handler in the stack, all you need to do is pass an error to the next callback provided as the last argument in the route handler callback. If you do not have any custom middleware installed, express will use the default which is already included in the middleware stack.

Using Custom Error Handlers

To use a custom function, you'll trigger it the same way, but you'll have to define the function yourself with the following criteria:

  1. it must take 4 arguments: (error, req, res, next) => {}
  2. it must be attached at the end the middleware stack

From there, you can parse out data from the req and error objects to obtain more information about the request and what error was thrown during its processing. More about this implementation below.

Starting with the basics, from the expressjs documentation:

Error Handling refers to how Express catches and processes errors that occur both synchronously and asynchronously. Express comes with a default error handler so you don’t need to write your own to get started.

Looks like you're mainly concerned with handling asynchronus errors. You can attach a custom error handler to the end of your middleware stack that takes 4 arguments, like so:

// apiRouter would contain all your routes
app.use('/api/v1', apiRouter);

// error handler gets attached at the end of the stack, after your routes
app.use((err, req, res, next) => {
  // custom error handling logic here
  // make sure to end the connection, or the request will hang
});

and express will automatically call that middleware anytime you pass an error to the next function, from any one of your route handlers.

Another thing to note from their documentation:

Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error.

Depending on your version, you may begin to encounter this behavior (if you're returning Promises). In that case, for those Promises, your errors are being handled by the default express error middleware.

If you pass an error to next() and you do not handle it in a custom error handler, it will be handled by the built-in error handler; the error will be written to the client with the stack trace. The stack trace is not included in the production environment.

Default Error Handler Description

When an error is written, the following information is added to the response:

  • The res.statusCode is set from err.status (or err.statusCode). If this value is outside the 4xx or 5xx range, it will be set to 500.
  • The res.statusMessage is set according to the status code.
  • The body will be the HTML of the status code message when in production > environment, otherwise will be err.stack.
  • Any headers specified in an err.headers object.

Suggestion

In your case, I would leverage the ability to create multiple error handlers and provide a "catch-all" at the end of that chain. When catching errors, you can add custom props that gives hints to error handlers as to what they are dealing with. Explicit if statements within the handlers can check for those custom props and handle errors you expect to see. Unforeseen errors will fall to the next error handler (provided you're passing the error to it by using next).

app.use('/api/v1', apiRouter);

...

app.use(errorLoggingHandler);
app.use(errorResponseHandler);
app.use(errorCatchAllHandler);

CodePudding user response:

Sorry if I did't explain the idea correctly. My goal is to pass through the error response from service2 to service1 when an error has occurs. I solved usng res.status(error.response.status).send(error.response.data) in service 1 inside the catch. Thanks!

  • Related