I am creating a router with ExpressJs (using TypeScript) and thrown exceptions are not caught by my custom error handler, here is the code:
In index.ts file (which is the main):
import express, { Express } from 'express';
import cors from 'cors';
import { config } from '~/utils/config';
import { NotFoundRoute } from '~/middlewares/NotFound';
import { ExceptionHandler } from '~/middlewares/ExceptionHandler';
import { usersRouter } from '~/controllers/users';
const app: Express = express();
app.use(express.json());
app.use(cors());
app.use('/users', usersRouter);
app.all('*', NotFoundRoute);
app.use(ExceptionHandler);
app.listen(config.port, () => console.log("API is running on port " + config.port));
The custom exception handler:
import { NextFunction, Request, Response } from 'express';
export const ExceptionHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
if (res.headersSent)
return next(err);
if (err.status && err.message) {
res.status(err.status).send({ error: err.message });
} else
return res.status(500).send({ message: "Internal Server Error" });
}
The router that is problematic (where InternalServerError is not caught by the above):
import { Request, Response, Router } from 'express';
import { User } from '~/schemas/User';
import { InternalServerError } from '~/errors/InternalServerError';
const usersRouter = Router();
usersRouter.get('/', async (req: Request, res: Response) => {
try {
const users = await User.find({});
res.status(200).send(users);
} catch (err) {
res.status(500).send({ message: "Failed to fetch users"});
}
})
usersRouter.post('/register', async (req: Request, res: Response) => {
try {
const { email, password } = req.body;
if (!email || !password)
res.status(403).send({ message: "Bad Request"});
const newUser = User.createUser({ email, password });
await newUser.save();
res.status(201).send(newUser);
} catch (err) {
throw new InternalServerError();
}
})
export { usersRouter };
All my exception are not caught which means that it is not exception-related.
In the index.ts file, the NotFoundRoute throws an exception that is caught, so I guess it works on the file's context. How it is not working ? I suppose the router has a thrown exception that would be caught but it is not.
To remind the context, I am trying to force errors to happen to see if the error handling is correct. So in this case I forced
newUser.save()
to fail.
Express cannot handle thrown exceptions or rejected Promises. To tell express that there is an error you need to call the next() function instead of using throw or rejecting a Promise:
usersRouter.post('/register', async (req: Request, res: Response, next: Function) => {
try {
const { email, password } = req.body;
if (!email || !password)
res.status(403).send({ message: "Bad Request"});
const newUser = User.createUser({ email, password });
await newUser.save();
res.status(201).send(newUser);
} catch (err) {
next(new InternalServerError()); // This is how you "throw" errors
// in Express
}
})
Note that express only expect you to call the function it passes as the third argument. The name of the argument is up to you. You can use words other than next:
usersRouter.post('/register', async (req: Request, res: Response, error: Function) => {
try {
/* some logic */
} catch (err) {
error(new InternalServerError());
}
})
Though traditionally the name of the third argument is next and most javascript developers expect it to be that.
Adding to slebetman's answer: You can throw errors that will be handled by the exception handler, but only in synchronous middleware. You can try it out:
app.use("/sync", function(req, res) {
throw "sync";
})
.use("/async", async function(req, res) {
throw "async";
})
.use(function(err, req, res, next) {
res.end("Caught " + err);
});
Related
So I wrote middleware in express (works just fine) for REST API endpoints like:
DELETE "/:id", PUT "/:id", GET "/:id". I connect via mongoose with MongoDB database. If no todo document by such id were found in database I throw error, terminate further actions.
Question: In code review it was said that I should use Generic middleware instead - what does it mean? Maybe any one can explain or post a link to some helpful article.
import { Request, Response, NextFunction } from 'express';
import Todo from '../models/Todo';
export const isExistMiddleWare = async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = req.params;
const isExist = await Todo.findById(id);
if (!isExist) {
throw new Error('no todo with such id');
}
next();
} catch (err) {
res.status(404).json({ error: err.message });
}
};
I do have errorHandling middleWare already in my code (also works fine)
import type { ErrorRequestHandler } from 'express';
export const errorHandler: ErrorRequestHandler = (Error, req, res, next) => {
res.status(Error.status || 500).json({ error: Error.message || 'Something went wrong' });
};
Im creating a video sharing app that allows users to sign in and update their profiles. i've created an error handler to identify errors in the backend and when im submitting my put request to update a user its going off.
here are my routes that im using
import express from 'express';
import { update} from '../controllers/user.js'
import { verifyToken } from '../verifyToken.js';
const router = express.Router();
router.put("/:id", verifyToken, update);
here is the controller for updating the user
import { createError } from "../error.js";
import User from '../models/User.js'
export const update = async (req, res, next) => {
if(req.params.id === req.user.id) {
try {
const updatedUser = await User.findByIdAndUpdate(req.params.id, {
$set: req.body
}, {new: true})
res.status(200).json(updatedUser)
} catch (error) {
next(error)
}
} else {
return next(createError(403, 'You can only update this account'));
}
}
here is the custom error handling im using which is in the index.js below and error.js
app.use((err, req, res, next) => {
const status = err.status || 500
const message = err.message || "something went wrong"
return res.status(status).json({
success: false,
status,
message
})
})
export const createError = (status, message) => {
const err = new Error()
err.status=status
err.message=message
return err
}
here is the middleware for the token
import jwt from 'jsonwebtoken'
import { createError } from './error.js'
export const verifyToken = (req, res, next) => {
const token = req.cookies.acess_token
if(!token) return next(createError(401, 'You are not authenticated'))
jwt.verify(token, process.env.JWT, (err, user)=>{
if(err) return next(createError(403, "Token invalid"))
req.user = user
next()
})
}
Custom error handler:
export const errorHandler: ErrorRequestHandler = (err, _, res) => {
if (err instanceof HttpError) {
res.status(err.statusCode).json({
message: err.message
});
return;
}
res.status(500).json({
message: err.message,
});
};
Handler where I throw the error:
export const registerHandler: Handler = async (req, res) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
throw new UnprocessableEntity();
}
try {
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
} catch (error) {
throw new BadRequest(error.message);
}
};
The error handler middleware works as expected when everywhere except when it is thrown in catch block of the registerHandler. It's driving me crazy. Can somebody explain why this is so?
middlewares are pipes, in other words functions that runs after another function, so if you want to run an error handler you need to pass the next function to run
export const registerHandler: Handler = async (req, res, next) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
// to let express know that the next function to run is an errorhandler you need to pass a parameter to the function next
return next(new UnprocessableEntity());
}
try {
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
} catch (error) {
throw new BadRequest(error.message);
}
};
to create error handlers you need to create a function with 4 parameter
error: the error
req: request
res: response
next: next handler
function errorHandler (error, req, res, next) {
if (err instanceof HttpError) {
return res.status(err.statusCode).json({
message: err.message
});
}
return next(error);
}
for this to work you need to specify your error handlers after all your routes
const app = express()
app.use("/api", apiRoutes());
app.use("/more-routes", moreRoutes());
app.use(errorHandler);
app.use(anotherErrorHandler);
This might not be the exact solution you may be looking for, but it might help.
The express js documentation
says:-
Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error.
So you don't need try and catch at all.
The above code can be written as:-
export const registerHandler: Handler = async (req, res) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
throw new UnprocessableEntity();
}
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
};
If any error occurs in await line, the errorHandler will automatically be called, and you don't have to explicitly throw an error.
I am trying to handle error with class constructor in a better way. I named my class HttpError that is written below, the problem is HttpError doesn't work properly.
This is "app.js" file:
const express = require('express');
const app = express();
const placesRoutes = require('./routes/placesRoutes');
app.use('/api/places',placesRoutes);
app.get('/', (req, res, next) => {
res.json({message: 'the server is working.'});
next();
});
app.use((error, req, res, next)=> {
if(res.headerSent){
return next(error);
}
res.status(error.code || 500);// This error shows when I am entering wrong pid
res.json({message: 'Does not match any route'});
})
app.listen(5000);
and this is placesRoutes.js:
const express = require('express');
const HttpError = require('../models/httpError');
const router = express.Router();
const DUMMY_PLACE = [
{
id: '1',
name: 'jahid',
address: 'Rajoir, Rayenda, Sarankhola, Bagerhat',
email: 'gahid#gmail.com',
password: 'jahid5868'
},
{
id: '2',
name: 'saiful',
address: 'Rajoir, Rayenda, Sarankhola, Bagerhat',
email: 'gahid#gmail.com',
password: 'jahid5868'
},
{
id: '3',
name: 'sadiqul',
address: 'Rajoir, Rayenda, Sarankhola, Bagerhat',
email: 'gkaid#gmail.com',
password: 'jahid5868'
},
{
id: '4',
name: 'hasib',
address: 'Rajoir, Rayenda, Sarankhola, Bagerhat',
email: 'ahid#gmail.com',
password: 'jahid5868'
},
]
router.get('/:pid', (req, res, next) => {
const sid = req.params.pid;
const data = DUMMY_PLACE.find(p=> p.id === sid);
if(!data){
return next(new HttpError('Does not found uid', 404));
}
res.json({data});
});
module.exports = router;
and this is "HttpError.js":
class HttpError extends Error {
constructor(message, statusCode) {
super();
this.message = message;
this.statusCode = statusCode;
}
};
module.exports = HttpError;
When I am entering /api/places/:(wrong-pid). It doesn't show HttpError rather it's showing the error 500 from bottom of app.js.
I am new to express so please don't bother if my question doesn't make sense.
With the above code an instance of HttpError gets passed to the error handling middleware, but you do not really use it there. You probably wanted to do something like:
app.use((error, req, res, next)=> {
if(res.headerSent){
return next(error);
}
if(error instanceof HttpError) {
res.status(error.statusCode);
res.json({message: error.message});
} else {
res.status(error.code || 500);
res.json({message: 'Does not match any route'});
}
})
So basically you have many ways of handling Errors, I like to use try/catch blocks very simple to understand and very useful and clear. You're doing a block asking him to do something if it's getting an error it's passing it to the catch block.
For example:
const validateUser = async (req, res, next) => {
try {
const { email, password } = req.body
if (!email || !password) {
throw new ErrorHandler(404, 'Missing required email and password fields')
}
const user = await db.User.findOne({ where: { email }});
if (!user) {
throw new ErrorHandler(404, 'User with the specified email does not exists')
}
next()
} catch (error) {
next(error)
}
}
so we try something (getting data checking it) if there is an error its passing to the catch block.
another simplest example:
try {
capture = await payPalClient.client().execute(request);
captureID = capture.result;
// console.log(captureID)
} catch (err) {
// 4. Handle any errors from the call
return res.status(500).send({
status: 500,
error: err,
});
}
trying something (some paypal stuff from one of my code not important).
if I'm getting an error from it I return a response (500 with a message of error .)
you can check on google for error handling express try/catch
I would like some basic error handling on every route, so if there is ever an exception, the API at least responds with 500.
According to this pattern, you still need to include a try/catch block in every route:
app.post('/post', async (req, res, next) => {
const { title, author } = req.body;
try {
if (!title || !author) {
throw new BadRequest('Missing required fields: title or author');
}
const post = await db.post.insert({ title, author });
res.json(post);
} catch (err) {
next(err) // passed to the error-handling middleware
}
});
That seems a little repetitive. Is there a higher-level way where exceptions are automatically caught everywhere and passed to the middleware?
I mean, it would obviously be possible for me to just define my own appGet():
function appGet(route, cb) {
app.get(route, async (req, res, next) => {
try {
await cb(req, res, next);
} catch (e) {
next(e);
}
});
}
Is there some built in version of this?
You can use express-promise-router package.
A simple wrapper for Express 4's Router that allows middleware to return promises. This package makes it simpler to write route handlers for Express when dealing with promises by reducing duplicate code.
E.g.
app.ts:
import express from 'express';
import Router from 'express-promise-router';
import bodyParser from 'body-parser';
const router = Router();
const app = express();
const port = 3000;
app.use(bodyParser.json());
app.use(router);
router.post('/post', async (req, res) => {
const { title, author } = req.body;
if (!title || !author) {
throw new Error('Missing required fields: title or author');
}
const post = { title, author };
res.json(post);
});
router.use((err, req, res, next) => {
res.status(500).send(err.message);
});
app.listen(port, () => console.log(`Server started at http://localhost:${port}`));
You don't need try/catch statement block anymore.
Test result:
I think the better approach would be to divide the services and the controllers which is demonstrated below.
Add post service:
async function addPostService (title, author) => {
if (!title || !author)
throw new BadRequest('Missing required fields: title or author');
return await db.post.insert({ title, author });
};
Add post controller:
function addPost(req, res, next){
const { title, author }= req.body;
addPostService
.then((post) => {
res.json(post);
})
.catch(next) // will go through global error handler middleware
}
Now, we can make a global error handler middleware which will catch the error thrown by any controller throughout the app.
function globalErrorHandler(err, req, res, next){
switch(true){
case typeof err === 'string':
// works for any errors thrown directly
// eg: throw 'Some error occured!';
return res.status(404).json({ message: 'Error: Not found!'});
// our custom error
case err.name = 'BadRequest':
return res.status(400).json({ message: 'Missing required fields: title or author!'})
default:
return res.status(500).json({ message: err.message });
}
}
And, don't forget to use the error handler middleware right before starting the server.
// ....
app.use(globalErrorHandler);
app.listen(port, () => { console.log('Server started...')});