Global Middleware not being triggered - javascript

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!")
})

Related

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

Express Error Handling: Sending a Message to Frontend

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

Return JSON from Express error handling middleware instead of HTML

I am having a bit of an issue returning a JSON response from my Express handling middleware. Currently, I am getting an HTML error page in Postman. On my actual client, I only return a 500 error from the fetch request in the console. The JSON data that should be the error message does not come through as anticipated.
Here is my error handling function. It simply passes the error as a JSON response back to the client. Anytime next(some_error) is called in my controller routes, Express pipes them through this error handling function:
const express = require('express');
const router = express.Router();
exports.errorHandler = (err, req, res, next) => {
res.setHeader('Content-type', 'application/json');
res.status(500).json({ err });
};
module.exports = router;
Here is a portion of the controller route that I am throwing an intentional error in to test the error handling middleware:
if (isMatch) {
const payload = { id: user._id };
jwt.sign(
payload,
JWT_SECRET_KEY,
{ expiresIn: 900000 },
(err, token) => {
if (err) {
const error = new Error(JWT_FAILED);
error.httpStatusCode = 400;
return next(error);
}
payload.token = `Bearer ${token}`;
return res.status(200).json({
success: true,
accountant: payload
});
}
);
} else {
const error = new Error(PASSWORD_INCORRECT);
error.genericError =
'The provided password did not match the database.';
error.httpStatusCode = 400;
return next(error);
}
This is the page I am getting in response for reference:
I am not sure what I am doing wrong, I usually don't have an issue sending a JSON response back from Express. I have a hunch the errors handled by Express require an extra step somewhere to not default to returning as HTML and not JSON.
This fixed my issue. I removed router and added module.exports = errorHandler and this resolved the issue. Express was not calling my errorHandler middleware function. It was just seeing a next(some_error) in my controller routes and then returning the error it's default way. I assumed my errorHandler function was returning this when in fact, my function was never even called.
This is the updated error handling middleware:
const errorHandler = (err, req, res, next) => {
res.json({ err: 'and error' });
};
module.exports = errorHandler;
This now sends back JSON. Phewwww.

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
}
});

Categories

Resources