Express Error Handling: Sending a Message to Frontend - javascript

I'm having some trouble error handling my authentication API calls. When I send the 500 status from Express, my frontend (Vue in this case) only picks up the message Request failed with status code 500 rather than something more helpful for triage like this is the worst error ever (in the example below).
In the below example, when I call '/post' from the API, I throw an error which is handled by my custom middleware. The middleware successfully handles the error and sends the appropriate status to my frontend, but I can't figure out how to send useful messages (e.g. 'this is the worst error ever') / access them in the front end.
Is this a common use case? Am I doing anything obviously wrong? Does the message I send come up in the (err) parameter, or do I need to add a resp parameter?
Express Route for '/login'
router.post('/login', (req, res, next) => {
throw Error('this is the worst error ever')
})
Custom express error handler
app.use(function(err, req, res, next) {
res.status(err.status || 500).send({
error: {
status: err.status || 500,
message: err.message || 'Internal Server Error',
},
});
});
Handle the API Response in Vue
login (e) {
e.preventDefault();
UserService.login(this.username, this.password) //this is simple axios post call
.catch((err) => {
this.loginStatus = err.message
return
})
}

Found the answer to this for those that find this helpful. The err that is caught has a response variable. This is where the data is sent via the express send command. See corrected code below for the frontend:
login (e) {
e.preventDefault();
UserService.login(this.username, this.password) //this is simple axios post call
.catch((err) => {
this.loginStatus = err.response.data.message
return
})
}

I think you need to add:
Throw new Error()
instead of
Throw Error
If you are making an asynchronous calling you can do this
app.get('/', function (req, res, next) {
fs.readFile('/file-does-not-exist', function (err, data) {
if (err) {
next(err) // Pass errors to Express.
} else {
res.send(data)
}
})
})
Code should look like this:
router.post('/login', (req, res, next) => {
throw new Error('this is the worst error ever')
})
Check this in express documentation

Related

Global Middleware not being triggered

Server.ts
import express, { Request, Response, NextFunction } from 'express'
import { router } from "./routes"
import { Database } from './Database/index'
const app = express()
const db = new Database()
db.connect()
app.use(router) // contains all my routes
// The middleware I'm trying to be fired
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
if (err instanceof Error) {
return res.status(400).json({
error: err.message
})
}
return res.status(500).json({
status: 'error',
message: 'Internal Server Error :('
})
})
app.listen(3000, () => console.log('Server is running'))
According to Express4 Documentation
You define error-handling middleware last, after other app.use() and routes calls;
So I think it's right. But for some reason its NOT being called/triggered.
Why it's NOT working?
When I try to access an URL that doesn't exists inside routes.ts it gives a standard 404 error, but I want to get it from my middleware.
I want to use this Middleware for all errors, but most for when user makes a request for an endpoint that doesn't exists.
This is because in 404 is not an error. It just means there is no matching route.
From the docs:
In Express, 404 responses are not the result of an error, so the error-handler middleware will not capture them.
This is the default code from the docs. Add this at the bottom:
app.use(function (req, res, next) {
res.status(404).send("Sorry can't find that!")
})

Express Try and Catch in Form of middleware

i'm working on node.js using Express to built a backend.
i'm intended to handle status 500 error that may happened.
router.put('/test', async (req, res) => {
try {
return res.send(await request.updateTest(req.body, 1))
} catch(err) {
console.log(err)
return res.status(500).send(err.stack)
}
})
this is my example of the code. it's do work perfectly. but when i'm try to make unknown error from the database query, i want to log the error and return status 500 as response with the error detail.
but i'll need to add try and catch every time i'm build a new controller/routes
is there anyway i could express them in form of middleware instead of write try and catch everytime?
this is an example of code i've try to make it as middleware but it's has no work and no effect when called.
error.js
module.exports = function (err, req, res, next) {
console.log(err)
res.status(500).send({
error: 'Internal Server Error',
message: err.stack
})
next(err)
}
main.js
const errorHandler = require('./error')
const { corsOption } = require('./cors')
const cors = require('cors')
const test = require('./test')
module.exports = function (app) {
app.use(cors(corsOption))
app.use(errorHandler)
app.use('/api/test', test)
}
is there anyway that i can do for this to work?
Your global error handler should be placed after all other middlewares/routes:
app.use(middleware)
// all other middlewares
app.use('/api/test', test)
// all other routes
// error handler
app.use(function (err, req, res, next) {
res.status(500).json({
error: err.message,
});
});
To avoid adding try/catch to everything, better to wrap your route handler to catch the errors (asyncWrapper):
app.use(middleware)
// all other middlewares
const asyncWrapper = (cb) => {
return (req, res, next) => cb(req, res, next).catch(next);
};
const test = async (req, res) => {
return res.send(await request.updateTest(req.body, 1))
}
// wrap your handler to catch errors (Async functions return a promise)
app.use('/api/test', asyncWrapper(test))
// all other routes
// error handler
app.use(function (err, req, res, next) {
res.status(500).json({
error: err.message,
});
});
There are two approaches to resolve unhandled exceptions in Node.js
Using try-catch blockwhich is already you are using
Using Process i.e use Process to handle exception. A process is a global object that provides information about the current Node.js process. The process is a listener function that is always listening to the events. The most effective and efficient approach is to use Process. If any uncaught or unhandled exception occurs in your code flow, that exception will be caught in code
process.on('uncaughtException', function(err) {
// Handle the error safely
console.log(err)
})
The above code will be able to handle any sort of unhandled exception which occurs in Node.js. see this Process Events

How to compare and validate the requested URL with a custom URL string?

I have a route that goes to the index page. I have a secret token that allows access to this page. I want to compare the requested URL with a custom string. The current URL in use is http://localhost:3000/?token=secret but if I enter http://localhost:3000/as?token=secret it doesn't render the 404 error page that I created, instead says Cannot GET /as. I wondering how to validate this and render out the error page correctly
app.get('/', (req, res) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
return;
}
});
In Express each app.get or other related method handles it's own route. So when you do app.get('/' you are only matching routes that are / not /as.
You could change it to * to match all routes. Maybe like the following?
app.get('*', (req, res) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
return;
}
});
Or of course you could have a dedicated section for your 404 messages.
app.get('/', (req, res, next) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
return next();
}
// Valid request
});
app.get('*', (req, res) => {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
});
At the end of the day there are so many ways you can handle Express routing. It's very powerful and flexible.
I'd suggest you look here under the How do I handle 404 responses? section for another idea as well.
Also, remember, having secrets in a URL like that, probably isn't the most secure thing. So there are a lot of reasons I wouldn't suggest this for security reasons. But just answering your question, the above should work.
The idiomatic way to handle 404s in Express is to register your final route handler with the use method rather than using one of the HTTP-specific methods.
app.use((req, res) => {
res.render('error', {
title: '404 not found',
errorMessage: '404 not found'
})
})
I emphasize the word final because use registers a catch-all handler, so this will override any route that it precedes in your code. If all your other routes are registered before this, then this will catch any request that has not matched any other route – regardless of the HTTP method that was used. So this will apply to any GET, POST, PUT, DELETE request.
An even more idiomatic way in Express to handle 404s (and all HTTP error responses) is to use the next argument that comes with all route handlers. This will re-route the request to the next handler that specifically takes an error as it's first argument:
app.use((req, res, next) => {
const error = new Error('404 not found')
error.statusCode = 404
next(error)
})
app.use((error, req, res, next) => {
res.status(error.status || 500)
res.render('error', {
title: error.message,
errorMessage: error.message
})
})
This is great because you now have a generic error handler, which you can access from inside any other route. So this will handle not only 404s, but also 401s, 403s, 503s, anything you want that doesn't render successfully for the user. And you can access this route simply by calling next with an error as the first argument from inside any other route handler.
I suggest you to use passport-auth-token for validating the token & display success or error pages.
Configure Strategy
The token authentication strategy authenticates users using a token. The strategy requires a verify callback, which accepts these credentials and calls done providing a user.
passport.use('authtoken', new AuthTokenStrategy(
function(token, done) {
AccessToken.findOne({
id: token
}, function(error, accessToken) {
if (error) {
return done(error);
}
if (accessToken) {
if (!token.isValid(accessToken)) {
return done(null, false);
}
User.findOne({
id: accessToken.userId
}, function(error, user) {
if (error) {
return done(error);
}
if (!user) {
return done(null, false);
}
return done(null, user);
});
} else {
return done(null);
}
});
}
));
Authenticate Requests
Use passport.authenticate(), specifying the 'authtoken' strategy, to authenticate requests.
For example, as route middleware in an Express application:
app.post('/login',
passport.authenticate(
'authtoken',
{
session: false,
optional: false
}
),
function(req, res) {
res.redirect('/');
}
);

Express.js - End route chain from middleware

I have an Express.js 4 app, with an error logging middleware of the following form:
...
const logErrors = (err, req, res, next) => {
logger.error(err);
res.status(400).json({ error: err.message });
}
app.use(logErrors);
I was previously under the (incorrect) assumption, that by not calling next() the request would simply not continue onto my route handler. This is not the case as when an error occurs, it gets logged and sent as a response to the user but the request continues to /myroutehandler where it causes problems.
How do I stop the route/middleware chain after I've logged and responded to the error in my middleware? or what is the correct way to go about this?
Bonus question: Since I am not calling next() shouldn't the request hang? how/why is express handling this?
EDIT:
More of my code, to figure out why request jumps from the middleware to the route
app.use(morgan('combined', { stream: logger.inputStream }));
app.use(authMiddleware);
app.use(xmlParser({
normalize: false,
normalizeTags: false,
trim: true,
mergeAttrs: true,
explicitArray: false
}));
app.use(logErrors);
app.post('/HotLeadsFeed', async (req, res) => {
if (!req.body || !req.body.pi || !req.body.pi.pa) {
//error logging
}
try {
//logic
} catch (err) {
//error handling
}
});

Error Handling in Strongloop Loopback

I know the difference between a callback and a middleware next() function.
If a write a custom remote-method in Loopback, I can easily send errors in callback e.g callback(error,null) but in remote hooks or observers whenever I send error in next() function e.g
var err = new Error('This is error');
next(err)
it always says that Internal server error but it does not tell me what error is.
In order to view error I have to login to server and view logs.
Please tell me how can I send error as a response in next() function so that the on frontend I know what error has occurred.
Maybe use a middleware to hook in:
app.use( function(err,req,res){
res.json(err);
});
(This needs to he the last middleware defined...)
basically you can define callbacks with err and result.
For example in loopback,
if I have a model call "Action" you can simply send err or result to front end using json.
app.get('/your/api/call', function (req, res, next) {
var getTeam = function (cb) {
app.models.Team.find({}, function (err, teams) {
if (err) {
cb(err);
} else {
cb(null, teams);
}
});
};
async.waterfall([
getTeam
], function (err, team, role) {
if (err){
res.send(err); //send error to front end
} else {
res.send(team); //send result to front end
}
});
});
This approach can use with "app.use" function in root level also.

Categories

Resources