How to trigger Express error middleware? - node.js

I'm trying to unit test this piece of code in Mocha:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
I don't know how to get my request inside a Mocha unit test to trigger it.

First I would break out the middleware into its own file/function. As it sits, it's "integrated" with the Express app. So you're not testings only the error middleware, but also the Express app instance to an extent.
With that said, decouple the error middleware from the Express app:
src/middleware/error-handler.js
module.exports = (err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something broke!')
}
You will still .use() it in the main app.js or wherever you setup Express:
const express = require('express')
const errorHandler = require('./src/middleware/error-handler')
const app = express()
app.use(errorHandler)
But now we're free of the Express dependency and we have a simple function we can isolate and test. Below is a simple test with Jest which you can easily adjust to work with Mocha.
__tests__/middleware/error-handler.test.js
const errorHandler = require('../../src/middleware')
describe('middleware.ErrorHandler', () => {
/**
* Mocked Express Request object.
*/
let req
/**
* Mocked Express Response object.
*/
let res
/**
* Mocked Express Next function.
*/
const next = jest.fn()
/**
* Reset the `req` and `res` object before each test is ran.
*/
beforeEach(() => {
req = {
params: {},
body: {}
}
res = {
data: null,
code: null,
status (status) {
this.code = status
return this
},
send (payload) {
this.data = payload
}
}
next.mockClear()
})
test('should handle error', () => {
errorHandler(new Error(), req, res, next)
expect(res.code).toBeDefined()
expect(res.code).toBe(500)
expect(res.data).toBeDefined()
expect(res.data).toBe('Something broke!')
})
})

In your route you can pass an error object in the next parameter. For example:
app.get('/some-route', (req, res, next) => {
const error = {....};
next(error);
});
or just throw an error:
app.get('/some-route', (req, res, next) => {
const error = {....};
throw new Error(error);
});

Related

Route in Subroute with Middleware in NodeJs Express

I don't to put all by main routes in my server.js file, so the main file of my express server, because they are too many. Now I created a subroute named auth where the login, register, emailverification and passwordreset are in.
I created two functions to check if the user is logged in or not and now I need to check that the user is not logged in when registering or logging in, but the emailverification he needs to. Therefor, I can't set it as a middle ware in the app.use() function.
server.js:
const userRoute = require("./routes/user");
const authRoute = require("./routes/auth");
const app = express();
(async () => await connection())();
app.use("/auth", authRoute);
async function checkAuthenticated(req, res, next) {
const auth = await checkJWT(req.cookies.token);
if (auth === 500) return res.status(500).send();
if (!auth) return res.status(401).send();
req.user = auth;
return next();
}
async function checkNotAuthenticated(req, res, next) {
const auth = await checkJWT(req.cookies.token);
if (auth === 500) return res.status(500).send();
if (auth) return res.status(401).send();
return next();
}
async function checkJWT(token) {
try {
...
return user;
} catch (err) {
console.log(err);
return 500;
}
}
app.use("/user", checkAuthenticated, userRoute);
app.listen(3001, () => {
console.log("backend running on Port: 3001");
});
Now I import the checkNotAuth function to the /auth route file and add it to the routes...
const express = require("express");
const { checkAuthenticated, checkNotAuthenticated } = require("../server");
const router = express.Router();
router.post("/login", checkNotAuthenticated, async (req, res) => {
...
return res.status(401).send();
});
module.exports = router;
Now I get this error:
Error: Route.post() requires a callback function but got a [object Undefined]
when I remove the checkNotAuth middleware, there is no error.
How can I fix this?
Because I can't export these two functions from the server.js file for some reason, I just put them in there on file and exported them there. Now I import them in the server and auth route file.

Express make try/catch block into middleware to avoid duplication

Im writing my expressJs application, and Im finding in my routes controller the same duplicated code for catching exception, I was wondering how to avoid this.
I have checked this thread, but I get this error "Cannot read property 'catch' of undefined" : Express Try and Catch in Form of middleware
this is my route.js
const express = require("express");
const createHttpError = require("http-errors");
const Validator = require("../middlewares/Validator");
const TaskNotFoundException = require("../services/TaskNotFoundException");
const TaskService = require("../services/TaskService");
router.get("/tasks", async (req, res, next) => {
try {
const data = await TaskService.getTasks();
res.send({ code: 200, message: "Success", data });
} catch (error) {
next(createHttpError(500));
}
});
router.get("/task/:id", async (req, res, next) => {
const { id } = req.params;
try {
const data = await TaskService.getTask(id);
res.send({ code: 200, message: "Success", data });
} catch (error) {
if (error instanceof TaskNotFoundException) {
next(createHttpError(404));
} else {
next(createHttpError(500));
}
}
});
and the list goes on
as you see in all my routes I have a try catch block with the possible errors (either only a 500, or a 500/404). And I would like to avoid this repetition.
this is my app.js
const express = require("express");
const bodyParser = require("body-parser");
const createHttpError = require("http-errors");
const api = require("./routes/api");
const app = express();
app.use(express.json());
app.use(bodyParser.json());
app.use("/api", api);
// Catch HTTP 404
app.use((req, res, next) => {
next(createHttpError(404));
});
// Error Handler
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
error: {
status: err.status || 500,
message: err.message,
},
});
});
module.exports = app;
Like I said, it works perfectly now, I would just like to try to avoid the try catch code duplication, and Ive checked the other questions in Stackoverflow but havent helped. The solution ive linked returns a 500 with this catch undefined message (which is not what I want) and on other routes that also have a 404 it just doesnt work.
Thanks a lot!
Update:
I followed Heikos advice but still not working
api.js
const express = require("express");
const createHttpError = require("http-errors");
const Validator = require("../middlewares/Validator");
const TaskNotFoundException = require("../services/TaskNotFoundException");
const TaskService = require("../services/TaskService");
const router = express.Router();
router.get("/tasks", async (req, res, next) => {
const data = await TaskService.getTasks();
res.send({ code: 200, message: "Success", data });
});
app.js
const express = require("express");
const bodyParser = require("body-parser");
const createHttpError = require("http-errors");
const api = require("./routes/api");
const app = express();
app.use(express.json());
app.use(bodyParser.json());
app.use("/api", api);
function catchAsyncErrors(middleware) {
return async function(req, res, next) {
try {
await middleware(req, res, next);
} catch(err) {
next(err);
}
};
}
// Catch HTTP 404
app.use(catchAsyncErrors((req, res, next) => {
next(createHttpError(404));
}));
// Error Handler
app.use(catchAsyncErrors((err, req, res, next) => {
res.status(err.status || 500);
res.json({
error: {
status: err.status || 500,
message: err.message,
},
});
}));
module.exports = app;
If the code inside your async middleware functions contains an await, you must also wrap it in a try-catch block, otherwise a rejected promise will be unhandled. For example:
app.use(async function(req, res, next) {
try {
await Promise.reject("error");
} catch(err) {
next(err);
}
});
propagates the error to the error handler, but without the try-catch block it leads to an "UnhandledPromiseRejection".
You can save some typing if you wrap your middleware into a catchAsyncErrors function:
function catchAsyncErrors(middleware) {
return async function(req, res, next) {
try {
await middleware(req, res, next);
} catch(err) {
next(err);
}
};
}
router.get("/tasks", catchAsyncErrors(async (req, res, next) => {
const data = await TaskService.getTasks();
res.send({ code: 200, message: "Success", data });
}));

Custom API responses

I am currently trying to create an API handler that will ensure that all my requests follow the same template using express in Node JS. So I have defined the structure of the response and registered it as middleware in my express application. I am however unable to get the responses to work.
I have tried importing express into the external class for it to use the res parameter for the middleware. I have also tried it without. I have tried expressing it with middleware parameters such as (req, res, next) which didn't work either. So I am unsure what to try next.
The external handler is as follows:
exports.success = (message, results, statusCode) => {
return {
message,
error: false,
code: statusCode,
results
};
};
I however tried the following as well which didn't work:
exports.success = (message, results, statusCode) => {
return res.json({
message,
error: false,
code: statusCode,
results
});
};
I tried this as well which didn't work:
exports.success = (message, results, statusCode) => {
return (req, res, next) => {
res.json({
message,
error: false,
code: statusCode,
results
});
next();
}
};
I have implemented it in the middleware as follows:
this.app.use((req, res, next) => {
res.success = responseHandler.success;
next();
});
Is it not possible to implement what I am trying (which I doubt)? I have a feeling I am just returning the wrong thing but I am not sure what it is.
I think you should be able to get this working, I've tested a simple version of what you're trying to achieve.
I've used a bind() call to ensure the context is correct for the success() call, also we'll use the function declaration rather than an arrow function in the response-handler module for the same reason.
index.js
const express = require("express");
const port = 3000;
const app = express();
const bodyParser = require('body-parser')
const responseHandler = require('./response-handler.js')
app.use(bodyParser.json());
app.use((req, res, next) => {
res.success = responseHandler.success.bind(res);
next();
});
app.get('/success', function(req,res,error) {
res.success("Success - yay!!", { foo: 'bar' }, 418);
})
app.listen(port);
console.log(`Serving at http://localhost:${port}`);
response-handler.js
exports.success = function(message, results, statusCode) {
this.json({
message,
error: false,
code: statusCode,
results
});
};

Nodejs AsyncLocalStorage getStore() return undefined

I read this post AsyncLocalStorage for Easy Context Passing in Node.js
I try to get logId in my logs, but i cant, beacause asyncLocalStorage.getStore() return undefined.
It seems that context was lost inside MyLogger class.
How to solve it?
Here is my express App
const asyncLocalStorage = new AsyncLocalStorage();
app.use((req, res, next) => {
asyncLocalStorage.run(new Map(), () => {
asyncLocalStorage.getStore().set("requestId", uuid());
next();
});
});
module.exports.asyncLocalStorage = asyncLocalStorage;
Here is MyLogger class
static log(logId, className, text) {
const { asyncLocalStorage } = require("../server.js");
const store = asyncLocalStorage.getStore()
console.log(this._getBaseStaticLog(logId, logTypes.LOG, text, className));
}
I solve the problem.
The problem was that i lose context due to bodyparser middleware.
I change that middleware before set context, and now its ok.
Was:
app.use((req, res, next) => {
asyncLocalStorage.run(new Map(), () => {
asyncLocalStorage.getStore().set("requestId", uuid());
next();
});
});
// body parser
app.use(
bodyParser.json({
limit: "10mb"
})
);
Change:
// body parser
app.use(
bodyParser.json({
limit: "10mb"
})
);
app.use((req, res, next) => {
asyncLocalStorage.run(new Map(), () => {
asyncLocalStorage.getStore().set("requestId", uuid());
next();
});
});
And now its ok)

Mongoose errors doesn't raise from router to app

There's single api application like this:
const express = require('express')
const app = express()
const router = require('express').Router()
...
route.post('/dogs', (req, res, next) => {
const dog = new Dog() // it defined in real app
dog.validate() // error happens here
.then(() => {
return res.status(201)
})
// [1]
})
...
app.use('/api/v1', router)
app.use(notFoundErrorHandler)
app.use(globalErrorHandler)
function notFoundErrorHandler (req, res, next) {
res.status(404)
res.send({error: 'Not found'})
}
function globalErrorHandler (err, req, res, next) {
if (err) {
res.status(err.status || 500)
res.json({error: err.message || err})
}
}
If there's validation error it won't pass to the globalErrorHandler, but catching and rethrowing error solves the problem [1]:
.catch(err => { return next(err) })
Does this behaviour is normal for mongoose with not complete Promise implimentation, or it could be implimentated in another way?
It doesn't raise error when using other methods like save, find, etc.
Thats normal, yes and has nothing to do with mongoose but with Express.
Express doesn't handle unhanded exceptions implicit, you will have to handle them explicit. So in each route you will have to catch any errors and pass them to next.
I know that this may be frustrating sometimes so I would suggest that you create a route manager of sorts that will register each route with an action handler and a try/catch pattern.
There are many examples out there, here is one simple I like.
var dogHandler = require('dogHandler')
var serverRoutes = [{
path: "dogs",
method: "post",
action: dogHandler
}]
serverRoutes.forEach(route => {
app[route.method](route.path, (request, response, next) => {
route.action(request, response)
.then(() => next)
.catch(err => next(err));
});
});

Resources