Error Handling in nested API Calls with express and nodeJS - node.js

I have 3 microservices with an API interface with the goal to create a new user in my system.
service1 --> is the main service and will orchestrate the request along the other services
service2 --> will create the user in Firebase
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'
})
}
})
})

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:
it must take 4 arguments: (error, req, res, next) => {}
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);

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

Error: Can't set headers after they are sent because of res.?

I'm trying to set up a method that is called with Shopify's webhook. I get the data and I'm able to store with a fresh server but I get "Error: Can't set headers after they are sent" returned in the console. I believe this is because I'm calling res twice. Any ideas on how to structure this better?
This is my method:
function createProductsWebHook(req,res,next) {
//if(req.headers){
// res.status(200).send('Got it')
// return next()
// }
res.sendStatus(200)
next()
const productResponse = req.body
console.log(productResponse)
const product = Product.build({
body_html: req.body.body_html,
title: req.body.title,
});
product.save()
.then(saveProduct => res.json(saveProduct))
.catch((e)=> {
console.log(e)
});
}
This occurs because the middleware, createProductsWebHook(), is called first when a request is received, which then sends a 200 status code response, res.sendStatus(200). Then in, in the same middleware function, product.save().then(...) is called. save()’s callback function attempts to send a response too – after one has already been sent by the very same middleware – using res.json(saveProduct).
Key Takeaway
Middleware should not send the response; this defeats the purpose of middleware. Middleware's job is to decorate (add or remove information, i.e, headers, renew some auth session asynchronously, perform side effects, and other tasks) from a request or response and pass it along, like a chain of responsibility, not transmit it – that's what your route handler is for (the one you registered your HTTP path and method with, e.g., app.post(my_path, some_middleware, route_handler).

In Node JS, how can I redirect to a specific URL while outside of a route?

I'd like to redirect to a specific URL and flash a message when an error occurs. However, in my error-handling file, I do not have access to the res variable that is inside routes.
I tried performing HTTP requests to some route where I would queue the flash message and then call res.redirect() or res.render(), but the flash message never made it to the view.
So, how can I do the equivalent of a simple res.redirect() inside this other file?
Perhaps something like this in my error-handling file:
async function handler() {
await handle_error();
.then(fetch(someURL));
}
and something else like this in my routes file:
router.get(someURL, function(req, res) {
req.flash('error', "An error has occurred");
res.redirect("/");
}

Node/Express - Can't set headers after they are sent

I have been dealing with this problem now for quite a while, and I can't seem to figure out why it's happening.
I'm getting the Error: Can't set headers after they are sent.
I was able to track down the offending Function Call via the Stack Trace, which leads me to believe that the error lies within this function:
exports.getCardUser = function(req, res, next) {
if (req.user) {
User.cardUser(req.user.userId, function(responseValue) {
res.json(200, responseValue);
});
} else {
res.send(401);
}
};
However, if I hit the Endpoint via a regular REST Client, iex: Hitting the API Endpoint in an Isolated Environment, the Error is not being thrown.
Any ideas?
Edit: skypjack's brought me on the right track - Callback was called twice. Thanks!

Express Error Handling - Get Status Code

If I define error handling middleware with express like so:
app.use(function(err,req,res,next){
// Do Stuff
});
How do I get the HTTP status code of the error(or do I just assume is's a 500)?
Thanks,
Ari
In short, your code has to decide the appropriate error code based on the specific error you are handling within your error handling middleware.
There is not necessarily an HTTP status code generated at this point. By convention, when I call next(error) from a middleware function or router handler function, I put a code property so my error handling middleware can do res.status(err.code || 500).render('error_page', error); or something along those lines. But it's up to you whether you want to use this approach for your common 404 errors or only 5XX server errors or whatever. Very few things in express itself or most middleware will provide an http status code when passing an error to the next callback.
For a concrete example, let's say someone tried to register a user account in my app with an email address that I found already registered in the database, I might do return next(new AlreadyRegistered()); where the AlreadyRegistered constructor function would put a this.code = 409; //Conflict property on the error instance so the error handling middleware would send that 409 status code. (Whether it's better to use error handling middleware vs just dealing with this during the normal routing chain for this "normal operating conditions" error is arguable in terms of design, but hopefully this example is illustrative nonetheless).
FYI I also recommend the httperrors npm module which provides nicely-named wrapper classes with a statusCode property. I have used this to good effect in a few projects.
You could try to define the status code in the route:
app.get('/force-error', function(req, res) {
res.status(400)
next(new Error('Bad Request.'))
})
Then:
server.use(function serverErrorHandler (err, req, res, next) {
const code = res.statusCode
// if status 200 change to 500, otherwise get the defined status code
res.status(code === 200 ? 500 : code)
// check if it's a ajax request
if (req.xhr)
res.json({
error: {
status: res.statusCode,
message: err.message,
stack: err.stack,
}
})
else
next(err)
})

Exit after res.send() in Express.js

I have a fairly simple Express.js app with a login component that I'd like to exit early if login fails. I'm seeing indications that the app isn't doing that and I haven't found a definitive answer that indicates whether calling res.send() halts any further processing. Here's my code as it stands now:
client.login( username, password, function( auth, client ) {
if( !auth ) {
res.send( 401 );
}
// DO OTHER STUFF IF AUTH IS SUCCESSFUL
}
If I read the source code correctly, it should end the request (aborting further processing), but I'm new to node, so I'm not quite ready to trust what I think I'm reading. To boil it down, I guess I'm mostly looking for a definitive answer from a more trustworthy source that my own interpretation of unfamiliar source code. If send() doesn't abort processing, what's the right way to do that?
Of course express can not magically make your javascript function stop executing from somewhere else.
I don't like the next([error]) solution because I think errors should be only used for circumstances you usually don't expect (like an unreachable database or something). In this case, a simple wrong password would cause an error. It is a common convention to not use exceptions/errors for ordinary control flow.
I therefore recommend to place a return statement after the res.send call to make your function stop executing further.
client.login( username, password, function( auth, client ) {
if( !auth ) {
res.send( 401 );
return;
}
// DO OTHER STUFF REALLY ONLY IF AUTH IS SUCCESSFUL
}
If you are using express as your framework, you should call next() instead.
Each handler in express receives 3 parameters (unlinke 2 for basic http) which are req, res and next
next is a function that when called with no arguments will trigger the next handler in the middleware chain.
If next is called with an arguments, this argument will be interpreter as an error, regardless of the type of that argument.
Its signature is next([error]). When next is called with an error, then instead of calling the next handler in the middleware chain, it calls the error handler. You should handle the 401 response code in that error handler. See this for more info on error handling in Express
EDIT: As #Baptiste Costa commented, simply calling next() will not cease the current execution but it will call on the next middleware. It is good practice to use return next() instead to prevent Node from throwing errors further on (such as the can't set headers after they are sent - error). This includes the above suggestion of error throwing which is common:
return next(new Error([error]));
For your specific case you can just add the 'else' statement:
client.login( username, password, function( auth, client ) {
if( !auth ) {
res.send( 401 );
}else {
// DO OTHER STUFF IF AUTH IS SUCCESSFUL
}
}
Or, in general, you can use 'return':
return res.send( 401 );
in these cases , i tend to use a try...catch bloc .
client.login( username, password, function( auth, client ) {
try{
if(error1){
throw {status : 401 , message : 'error1'}
}
if(error2){
throw {status : 500 , message : 'error2'}
}
}catch(error){
res.status(error.status).json(error.message);
}
}
Simply do something like this to stop further execution.
function (request, response, next) {
var path = request.body.path;
if(path === undefined){
response.status(HttpStatus.BAD_REQUEST);
response.send('path is required!');
}
next(response)
};
You only need to do a return to end the flow:
return res.send( 401 );
That will send the 401 response back and won't proceed forward in the flow.
Why is no-one suggesting using an 'else' block?
if(!auth){
// Auth fail code
res.send 'Fail'
} else {
// Auth pass code
res.send 'Pass'
}
'next' is used when creating your own middleware for the "app.use(function(req, res, next));".
If you have set up a route like "app.get(route, function(req, res));" then the code in the function is where you can have the code you are specifying without needing to use 'next'.

Resources