I'm writing middleware that I'm applying at the route level, like so:
router.get('/foo', myMiddleware, (req, res) => { ... });
so I can do some stuff with the request. But I also need to catch errors to do some special handling. I know I can add a handler afterwards like this:
... (req, res) => { ... }, myErrorHandler);
and it'll get called just fine.
But my question is, is there any way to have a single piece of middleware that can do all of this so I don't need two points of integration? I tried calling req.on('error', (err) => { ... }) within my middleware but it never seems to be called.
Express comes with a built-in error handler that takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.
// Router
router.get('/foo', myMiddleware, (req, res) => { ... });
// Router Error Handler
router.use(function (err, req, res, next) {
});
I ended up solving this by writing a helper function that wraps the actual handler. It looks like this:
function checkPage(handler: express.RequestHandler) {
return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
let _write = res.write;
res.write = chunk => {
if (req.query.verbose) {
return _write.call(res, `<p>${chunk}</p>`);
} else {
return true;
}
}
try {
await handler(req, res, next);
res.write('<hr/><p style="color:green;"><b>happy</b></p>');
} catch (err) {
res.write(`<p style="color:red;">${err}</p>`);
res.write('<hr/><p style="color:red;"><b>SAD!</b></p>')
}
res.end();
}
}
Then in my route handler, I just use it like so:
router.get('/foo', checkPage(async (req, res, next) => {
...
res.write('stuff');
...
}));
Related
I have an express backend application. The problem I have is that all the routes contains the same try-catch piece which causes code bloat in my program:
// routes.js
router.get('/', async (req, res, next) => {
try {
const data = extractData(req)
await foo(data)
} catch (err) {
next(err)
}
})
// controllers.js
async function foo(data) {...do smh}
As you see above, try { extractData() } catch (err) { next(err) } portion of the code exists in all of the routes defined in the app.
I tried to create a wrapper function that takes controller function as parameter and use it as:
// routes.js
router.get('/', controllerWrapper(req, res, next, foo))
// controller-wrapper.js
async function controllerWrapper(req, res, next, controllerFunc) {
try {
const data = extractData(req)
await controllerFunc(data)
} catch (err) {
next(err)
}
}
But this does not work due to function being invoked, and not being actually a callback.
How can I achieve this?
You should use a closure for this, so you can return the middleware function from controllerWrapper and use the controllerFunc inside the returned middleware
function controllerWrapper(controllerFunc) {
return async function (req, res, next) {
try {
const data = extractData(req)
await controllerFunc(data)
} catch (err) {
next(err)
}
}
}
router.get('/', controllerWrapper(foo))
I have a problem and I haven't found a solution yet. I want to catch all errors if occur any error in each route but it's very inconvenient when I have to do it many times.
How can i implement it as a middleware same like app.use(ErrorHandle); ?
Code in ErrorHandler.js:
export const ErrorHandler = func => async (req, res, next) => {
try {
await func(req, res, next);
} catch (error) {
next(error);
}
}
Code in index.js
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
Code in route need to catch error:
import { ErrorHandler } from './ErrorHandler';
export const uploadMedia = ErrorHandler(async (req, res) => {
// do something...
let error = new Error();
error.statusCode = 404;
error.message = 'Content not found!';
}
Sorry if misunderstood your question... When you do the code below which you provided, you are assuming that if an error reaches the end of the stack via next(err), such handler should be called. Hence it's the last declaration after all your routes.
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
That, however, won't catch unhandledExceptionErrors. You still need the good old
try {
// throw error somewhere
} catch (e) {
next(e);
}
Personally, I haven't tried this package but it seems so be a nice hack for Express router's inability to handle promise returns. About express-promise-router:
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.
I am writing a middleware function that looks for validation errors and if the error is found gives out a certain output else continues the program flow. I have two functions with the exact code but they check for different schemas.
My first function runs without any exception. However, when I try to execute the second function I get an error in the console.
const validateCampground = (req, res, next) => {
const { error } = campgroundSchema.validate(req.body);
if (error) {
const msg = error.details.map((el) => el.message).join(",");
throw new ExpressError(msg, 400);
} else {
next();
}
};
const validateReview = (req, res, next) => {
const { error } = reviewSchema.validate(req.body);
if (error) {
const msg = error.details.map((el) => el.message).join(",");
throw new ExpressError(msg, 400);
} else {
next(); //this is the point where the exception occurs
}
};
It is only inside the validateReview function where next middleware function is not recognised as a valid function.
The problem was not with the next() middleware but instead it was with the route as I was wrapping the route with the validateReview function.
I was doing something like this :
app.post(
"/campgrounds/:id/reviews",
validateReview(
catchAsync(async (req, res) => {
//my Logic here
})
));
Whereas , I should have been doing something like this :
app.post(
"/campgrounds/:id/reviews",
validateReview,
catchAsync(async (req, res) => {
//my logic here
})
);
hi if you want to use a middileware
exports.middileware = (req,res,next)=>{
try{
//middileware logic
next();
}catch(err){
//print the error
})
}
}
and call the exported middileware file in requires file to check the middileware function
const { middileware } = require('path');
and use like this
router.get('/routename',middleware,nextfunction) //router you can choose as you like get,post,patch anything
try this out
I got this error when I omitted "req" and "res" in the function's parameters. When I added them, the error disappeared. Since I was using typescript, the first scenario looked like this:
function traceRoute(next){
console.log(routeTrace);
next();
}
Corrected to:
function traceRoute(req, res, next){
console.log(routeTrace);
next();
}
I have a situation where I want to invoke multiple express middlewares depends on the request payload.
These middlewares are generated from the express validator checkSchema
method.
So I have written a middleware which gets access to the request object and I can read a property from the request payload then take a decision on which schema has to be run.
An implementation would like this.
let app = express();
let schema1 = checkSchema({
field1: {
in: ["body"],
exists: {
errorMessage: "field1 is missing",
}
}
});
let schema2 = checkSchema({
field2: {
in: ["body"],
exists: {
errorMessage: "field 2 is missing",
}
}
});
app.post("/resource", (req, res, next) => {
if(req.body.type === "TYPE1") {
// Invoke schema1 middleware
}
if(req.body.type === "TYPE2") {
// Invoke schema2 middleware
}
});
Here schema1 and schema 2 are not single middleware. It is a
middleware array.
If it was middleware, I could call schema1(req, res, next);
If anybody has gone through this, Please suggest me what is the approach to run a middleware array manually.
I have released express-validator v6.0.0, which should help addressing this kind of thing.
Now there is a .run(req) method, which should let you do things with express-validator in an imperative way.
For your use case, you could do the following:
app.post("/resource", async (req, res, next) => {
if(req.body.type === "TYPE1") {
await Promise.all(schema1.map(chain => chain.run(req)));
}
if(req.body.type === "TYPE2") {
await Promise.all(schema2.map(chain => chain.run(req)));
}
});
Since checkSchema returns an array of validation chains, the code is mapping each of them to their respective execution promise.
When all of the promises are finished, your code can continue executing and do whatever you want. Maybe check if there are errors with validationResult, render a different page accordingly, etc -- up to you!
According to this question Use an array of middlewares at express.js there is one repo: https://github.com/blakeembrey/compose-middleware:
According to the readme:
app.use(compose([
function (req, res, next) {},
function (err, req, res, next) {},
function (req, res, next) {}
]))
So, what you can do is:
app.post("/resource", (req, res, next) => {
if(req.body.type === "TYPE1") {
compose(schema1)(req,res,next);
}
if(req.body.type === "TYPE2") {
compose(schema2)(req,res,next);
}
});
Here is the problem that I am facing in express.
Somewhere in my express middleware, I want to check for the presence of a file.
//Setting up express middeleware...
app.use(f1);
app.use(f2);
...
function f1(req, res, next) {
...
//Here I want to check if 'somefile' exists...
fs.access('somefile', callback1, req, res, next);
}
//In the callback, I want to continue with the middleware...
function callback1(err, req, res, next) {
if (err) {
//Report error but continue to next middleware function - f2
return next();
}
//If no error, also continue to the next middleware function - f2
return next();
}
function f2(req, res, next) {
}
How do I pass req, res, next as arguments to the callback of fs.access?
The above code does not work. I suspect I need to use closures but how?
A totally different way of looking at the problem is: How do I use, for example, fs.access, itself as a express middleware function?
For me this approach have much more sense:
Assume you want to create a middleware in f1, and then have a middleware for error handling handleError, and any other middleware.
For f1 you already have the req, res in the closure so you will have access in fs.access callback.
function f1(req, res, next) {
fs.access('somefile', (err) => {
if (err) return next(err);
// here you already have access to req, res
return next();
}
}
function f2(req, res, next) {
// do some stuff if there is err next(err);
}
function handleError(err, req, res, next) {
if (err) {
// handle somehow the error or pass down to next(err);
}
}
app.use(f1); // you pass down the err |
app.use(f2); // ------------ |
app.use(handleError); // <----|