How do I "next()" my way into the error handler in this project? - node.js

I am trying to run a test that says
test("POST /register, malformed and edge cases", async () => {
await request(server).post(`/auth/register`).expect(400);
});
I am sending an empty request body. My goal is to trigger the error handler to respond with some sort of 4xx status code.
I am working using the boilerplate in this blog: https://jasonwatmore.com/post/2020/05/13/node-mongo-api-with-email-sign-up-verification-authentication-forgot-password
Here's what I'm doing:
I hit this route with a POST request: this.router.post("/register", registerUserSchema, this.register);
I expect that the registerUserSchema is engaged and indeed it is. I can tell because a console.log statement happens. Then
function registerUserSchema(req: Request, res: Response, next: NextFunction) {
const schema: ObjectSchema<any> = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
confirmPassword: Joi.string().valid(Joi.ref("password")).required(),
acceptTerms: Joi.boolean().valid(true).required(),
});
validateRequest(req, next, schema);
}
the function downstream of registerUserSchema is this: validateRequest(req, next, schema); which does occur as I expedct, leading to
this
function validateRequest(req: Request, next: NextFunction, schema: ObjectSchema<any>) {
const options = {
abortEarly: false, // include all errors
allowUnknown: true, // ignore unknown props
stripUnknown: true, // remove unknown props
};
const { error, value } = schema.validate(req.body, options);
console.log(error, "12rm");
if (error) {
next(`Validation error: ${error.details.map(x => x.message.replaceAll('"', "")).join(", ")}`);
// next(error);
} else {
req.body = value;
next();
}
}
export default validateRequest;
I know that this "Validation error:" text makes it out of the server because it shows up in Postman.
However, what I really want is to be able to modify the status code from 500 to 4xx. I presumed this errorHandler would do it because I say this.app.use(errorHandler); in my app.ts file. But it doesn't happen, my console.log doesn't do anything during a malformed request to /auth/register
function errorHandler(err: any, request: Request, response: Response) {
console.log("In the error handler, 5rm", err, typeof err);
}
If someone can enlighten me: How do I anticipate where next() will activate next in my application?

In this case the solution was
const err = new Error(`Validation error: ${error.details.map(x => x.message.replaceAll('"', "")).join(", ")}`);
err.name = "ValidationError";
next(err);
then the error handler figured out it was next as I expected it to

Related

What would be the response if a middleware fails in a REST API?

I am reading a code that has two files like below:
first file that uses the currentuser middleware:
const router = express.Router();
router.get("/api/users/currentuser", currentUser, (req, res) => {
res.send({ currentUser: req.currentUser || null });
});
export { router as currentUserRouter };
Second file that defines the middleware:
interface UserPayload {
id: string;
email: string;
}
declare global {
namespace Express {
interface Request {
currentUser?: UserPayload;
}
}
}
export const currentUser = (
req: Request,
res: Response,
next: NextFunction
) => {
if (!req.session?.jwt) {
return next();
}
try {
const payload = jwt.verify(
req.session.jwt,
process.env.JWT_KEY!
) as UserPayload;
req.currentUser = payload;
} catch (err) {}
next();
};
I understand that if there is a verified jwt token, the middleware will take the the payload out of it and add it to the req object. But what if it fails and it can't add the payload/current user to the req? What would happen for the following request and what will the res object look like?
router.get("/api/users/currentuser", currentUser, (req, res) => {
res.send({ currentUser: req.currentUser || null });
});
Could you edit this get request to show how can I catch the probable error if I am not the writer of the middleware?
If you had a catchall exception handler, and your middleware threw an exception, you would determine the response.
If your middleware threw an exception and you did not catch it, the system might just exit the process.
If your middleware did not throw an exception, and did not call next(), and did not respond, the request would hang.
If your middleware returned a response, and did not call next(), your send function would never get invoked.
The bottom line is that you need to dump the response on your server and see exactly how your middleware handles this.
In most of my auth middleware, I choose to not call next(), and return a 403 error. But there are some benefits by throwing an exception, then returning a 403 from a catchall handler.
You need to respond with an error HTTP status code, and an error message in the body. The exact status and message depends on the type of the exception and its parameters, so you need to catch it and check it.
The current express middleware does not handle errors, it just does not set the req.currentUser = payload;, so you won't know about the user. I don't think this is a proper solution for an authentication error.
In the documentation you can see how error are handled:
https://expressjs.com/en/guide/using-middleware.html
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something broke!')
})
So I would rewrite the code and if the JWT verification fails, then I return for example 401 unauthorized. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
I guess you are using this JWT library: https://github.com/auth0/node-jsonwebtoken According to the docs and the code there are 3 types of errors: TokenExpiredError, JsonWebTokenError, NotBeforeError for verify. Here you can check when they are thrown: https://github.com/auth0/node-jsonwebtoken/blob/master/verify.js , here are their definitions: https://github.com/auth0/node-jsonwebtoken/tree/master/lib
So in the catch block you just check the type of the error with instanceof e.g. if (err instanceof jwt.JsonWebTokenError) ... and send the message accordingly with the res.status(401) and put the next() to the end of the try block, because it should be called only if the verification does not fail.

When handle all promises and other stuff, Get request not returning data, still sending

I am trying to query some data in my data. But when I send get request to my server, It's not returning anything. But when I console log my data is printed on the console. I handle all promises and other stuff. But the results are the same.
Controller method: -
#Get('order/:state')
async getOrderByStatus(
#Res() response: Response,
#Param('state') state: number,
): Promise<OrderService[]> {
try {
const orderStatus = await this.orderServiceService.getOrderByStatus(
state,
);
if (orderStatus) {
console.log(orderStatus);
return orderStatus;
}
} catch (error) {
console.log(error);
response.status(401).send(error);
}
}
Service method: -
async getOrderByStatus(state: number): Promise<OrderService[]> {
try {
const orderState = await this.orderModel.find({ orderStatus: state });
if (orderState) {
return orderState;
}
throw new HttpException('Order not found', HttpStatus.NOT_FOUND);
} catch (error) {
console.log(error);
throw error;
}
}
Client-side:- (Request data is still sending.Not returning data from my controller)
I really need your help... Thank you..❤️
When you add #Res() to the route handler, you are telling Nest that you want to use the library-specific approach and you'll handle sending the response on your own. If you don't need any underlying engine specific methods, I'd suggest not using #Res() in the route handler, and if you do need some method, but still want Nest to send the response you can end up using #Res({ passthrough: true }), just make sure that you don't end up calling res.send yourself.
For handling the error, you can catch the error and re-throw it as an HttpException, that way Nest's built-in exception filter will send the proper response code and message for you

Capture error from nodejs back end api using vue js front end

I am using Vue.js and I need to capture an error of my backend api.
When I try capture the error message, I get an error:
[Vue warn]: Error in v-on handler (Promise/async): "TypeError: Cannot read property 'message' of undefined"
What am I doing wrong?
I have a nodejs router test as:
router.post("/register", async (req, res) => {
const email = 'teste';
if (email) {
//Throw error to test
return res.status(400).send({
data: {
message: "Já existe uma conta com esse email no nosso sistema",
},
});
}
})
In my vuejs view, I have:
try {
//AuthenticationService.register is an axios post
const response = await AuthenticationService.register({
nome: this.nome,
email: this.email,
password: this.password,
});
} catch (err) {
console.log("erro", err);
this.error = err.data.message;
}
The returned body will not be directly in the caught error message, i.e. it is likely a traditional error which is either an object, string or Exception.
You would need to consume the body of the response (the status code being 400 will throw an error, but the body of the response will still need to be consumed and parsed as JSON).
In axios specifically, this response is included with the error as a property, i.e. err.response. Assuming you are not modifying existing axios behaviour, it will also parse the JSON for you automatically, so you can simply do like so:
try {
//AuthenticationService.register is an axios post
const response = await AuthenticationService.register({
nome: this.nome,
email: this.email,
password: this.password,
});
} catch (err) {
console.log("erro", err);
this.error = err.response.data.message;
}
If you're using the latest versions of Node, you can use the new null propagation operator for extra safety in Vue:
try {
//AuthenticationService.register is an axios post
const response = await AuthenticationService.register({
nome: this.nome,
email: this.email,
password: this.password,
});
} catch (err) {
console.log("erro", err);
this.error = err?.response?.data?.message;
}
This will mean that if any property is null it'll propagate the null instead of throwing an error and potentially halting your program flow. This is also referred to commonly as Optional Chaining and I highly recommend it within Vue as it will prevent a single unexpected null value from breaking your entire view model.

Finalize on http interceptor not being hit when token expires

I'm trying my hand at MEAN for the first time and I'm coming up against some resistance with error handling on jwt token expiration. I've got the error handler here on the express server.
const handleUnauthorisedError = (err: any, req: any, res: express.Response, next: any) => {
if (err.name === "UnauthorizedError") {
if (err.message === "jwt expired") {
res.header("Token-Expired", "true");
}
console.error(err);
res.status(401);
return res.json({ message: `${err.name}: ${err.message}` }).end();
}
};
Which is then being added here after the routes.
app.use("/api", setupRoutes());
app.use(handleUnauthorisedError);
Within my angular interceptor I've got this error handler
private handle401Error = (err: HttpErrorResponse, req: HttpRequest<any>, next: HttpHandler) => {
console.log(err.headers);
if (err.headers.has("Token-Expired")) {
this.tokenSubject.next(null);
return this.authenticationService.refresh().pipe(switchMap(() => {
return next.handle(this.addToken(req));
}), catchError(() => {
return this.authenticationService.logoutExpired().pipe(finalize(() => {
this.router.navigate(["/login"]);
}));
}));
}
return this.authenticationService.logoutExpired().pipe(finalize(() => {
this.router.navigate(["/login"]);
}));
}
However finalize never seems to occur, unless I terminate the express server. It's as if the connection is persisting maybe?
Z
I think finalize does not occur because this operator works when the source completes or throws an error. More on this here.
Think of the finalize operator as the finally statement from try/catch.
In your code, you are only emitting values.
Another question that arises is why would you use finalize?
I'd use this when I'm dealing with http calls.
But, if for some reason you still want to use this operator, you could make sure that you throw an error from this.authenticationService.logoutExpired():
logoutExpired () {
// Some logic here..
return throwError('err');
}
Here is a little StackBlitz demo.

Send response to client after Promise resolves - Adonis, Node.js

I have some promise
getSomeInfo(data) {
return new Promise((resolve, reject) => {
/* ...some code... */
someObject.getData((err, info) => {
if (info) {
resolve(info)
}
else {
reject("Error")
}
})
})
}
I use this promise and want to send response to client from Controller (AdonisJS):
async create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Why response is not work?
Simply do this.
async create ({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info)
response.status(201).json({
code: 201,
message: "Data received!",
data: info
})
}
When marking a function as async the function must return a Promise, this can be done explicitly.
async create({ request, response }) {
return this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Or implicitly using the await keyword.
async create({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
}
If your console.log(info) inside of create() works and shows the data you want, but the response.status(201).json(...) does not send a response, then I can see the following possibilities:
You've already sent a response to this request (and thus cannot send another one)
The .json() method is having trouble converting info to JSON (perhaps because of circular references) and throwing an exception.
You aren't passing the arguments request and response properly and thus response isn't what it is supposed to be.
You can test for the second case like this:
create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
});
}).catch(e => {
console.log("Error in create()", e);
response.sendStatus(500);
});
}
Also, there is no reason for this method to be declared async as you don't show that you're using await or any of the features of an async function.
In the comments, you say that this function is called directly by a router (I assume an Express router). If that's the case, then the function arguments are not declared properly as they come as two separate arguments, not as properties of an object. Change the function declaration to this:
create (request, response) { ... }

Resources