Include db access in my middleware.js file - node.js

I need to add a new duplicate email control process in my middleware.js file.
This is my middleware.js node file :
module.exports = {
requiresLoggedIn(req, res, next) {
if (!req.session.loggedIn) {
console.log(" FORBIDDEN CAUSE YOU ARE NOT LOGGED IN ")
res.status(403).send({
errorCode: "403"
})
return
} else {
next() // continue the process
}
},
permission_valid(permission) {
return function(req, res, next) {
if (!req.session.user.permissions.includes(permission)) {
console.log(" FORBIDDEN CAUSE THE PERMISSION IS MISSING ")
res.status(403).send({
errorCode: "403"
})
return
} else {
next() // continue the process
}
}
},
duplicate_email(db, email) {
db.collection("users").findOne({
'email': email
}, function(findErr, result) {
if (!result) {
// next() // continue the process
return false;
} else {
return true;
}
});
}
}
duplicate_email() is not working cause I have no db.collection access in my middleware.js file, I have tried async await, I have tried plenty of things.
I have tried out to change my middleware to this format :
module.exports = function(app, db) {
function requiresLoggedIn(req, res, next) {
if (!req.session.loggedIn) {
console.log(" FORBIDDEN CAUSE YOU ARE NOT LOGGED IN ")
res.status(403).send({
errorCode: "403"
})
return
} else {
next() // continue the process
}
}
}
But as long as I do that, requiresLoggedIn(req, res, next) and permission_valid(permission) won't work any more in the web services, don't know why:
This is one of my working web services, how could I add the eMail duplicate control middleware to them , please? :
app.post("/createUser", middleware.requiresLoggedIn, middleware.permission_valid("CREATE_USER"), function(req, res) {
This is what I should have, but it doesn't work :
app.post("/createUser", middleware.requiresLoggedIn, middleware.permission_valid("CREATE_USER"),middleware.duplicate_email(db, req.body.email), function(req, res) {
I would appreciate if you could help me a few, thank you.

I've solved similar issue by attaching the db object on the base request which all the requests are inherits from.
import express from 'express';
express.request.db = db;
// ------^ this is the base request which all requests inherits from.
Then your db will be available in each request by accessing request.db in your middleware / controller

Related

Express router, Router.post executing before router.use middleware

My problem is that for some reason express router doesn't wait for next() function inside middleware and goes str8 to execution fo router.post.
Router.use('/posts/add', addPosts);
Router.post('/posts/add', (req, res) => {
if(req.success){
res.status(200).send('post added');
}else{
res.status(400).send({error: true, message: 'sth went wrong'})
}
});
Below is the middleware code:
module.exports = (req, res, next) => {
try {
if (req.authorization && req.authorization.access_level < 3) {
let post = new postsModel(req.body);
post.save().then(post => {
console.log(post);
req.success = true;
next();
});
} else {
throw new Error('unauthorized access');
}
} catch (err) {
res.status(400).send({ error: true, message: err.message });
}
};
Despite middlewares execution, router for some reason is always executing the router.post, doesn't wait for the next() function therefore awlays return error. Anybody could help with that?
Maybe try with the following approach:
Router.route('/posts/add', addPosts)
.post((req, res) => {
Refer to this documentation for further info.
I wanted to deeply apologize as I haven't posted the code for 1 more middleware invoked before anything else.
try {
const path = req.path;
switch (path) {
case '/add':
let data = req.body;
let error = addPostSchema.validate(data).error;
console.log(error);
if (error) {
throw new Error(error.message);
} else {
console.log('addPost validated');
next()
}
}
next();
} catch (err) {
console.log('walalala');
res.status(400).send({ error: true, message: err.message });
}
};
As you can see above, I've had double next() invoked. What I didn't know is that invoking next() does not stop rest of the code in middleware from executing, therefore it was prematurly invoking Router.post. Apologies for the confusion. Hope it will help somebody though having similiar problem.

How to throw error from services and repositories in nodeJS app following repository pattern

I am developing an API in NodeJS, expressJS and I am following repository pattern. So there are controllers, services and repositories.
Now, by the rules of repository pattern, My controllers, services and repositories are like this. These are just for demonstration purpose, actual code is different from this.
authController.js
export const loginController = async (req, res, next) => {
const user = await authServices.login(req.body);
if (user.error) {
res.error(user.errorBody)
}
else {
return res.success({ code: 200, message: "Logged in succesfully", data: user });
}
}
authServices.js
export const loginServcies = async (loginDetails) => {
const user = await userRepository.login(loginDetails);
if (!user) {
return {
error: true, errorBody: {
code: 422, message: 'User doesnot exist', errors: null
}
}
}
else if (!user.active) {
return { error: true, errorBody: { code: 403, message: 'User not active', errors: null } };
}
else if (user) {
return user;
}
}
userRepository.js
export const userRepository = async (loginDetails) => {
let user;
try {
user = await user.find({ where: { user: loginDetails.user } })
}
catch (error) {
return { error: true, errorBody: { code: 500 } }
}
}
Now the problem I am facing is I am repeating the code and returning the error from repo => services => controller.
What I want is, to throw the error from anywhere either it be repository or services.
As per the express documentation, we can use a global middleware in our app.js and pass the error to the next function like next(err), and express will automatically handle that.
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
but as next() function is not available in our services and repositories, how can I handle and throw the errors?
In the above example, I just passed two errors from services to controllers but in a practical case, there could be many, so I will repeat a lot of code, that I don't want to do
In controller, surround service call in a try-catch block and pass error to the next function.
Then, you can throw errors in service and repository functions and manage them in errorHandler middleware.

http-proxy-middleware: return custom error instead of proxying the request

The http-proxy-middleware Nodejs module provides a way of re-target request using a function in the option.router parameter. As described here:
router: function(req) {
return 'http://localhost:8004';
}
I'll need to implement a process that check some aspect in the request (headers, URLs... all that information is at hand in the req object that function receives) and return a 404 error in some case. Something like this:
router: function(req) {
if (checkRequest(req)) {
return 'http://localhost:8004';
}
else {
// Don't proxy and return a 404 to the client
}
}
However, I don't know how to solve that // Don't proxy and return a 404 to the client. Looking to http-proxy-middleware is not so evident (or at least I haven't found the way...).
Any help/feedback on this is welcomed!
You can do this in onProxyReq instead of throwing and catching an error:
app.use('/proxy/:service/', proxy({
...
onProxyReq: (proxyReq, req, res) => {
if (checkRequest(req)) {
// Happy path
...
return target;
} else {
res.status(404).send();
}
}
}));
At the end I have solved throwing and expection and using the default Express error handler (I didn't mention in the question post, but the proxy lives in a Express-based application).
Something like this:
app.use('/proxy/:service/', proxy({
...
router: function(req) {
if (checkRequest(req)) {
// Happy path
...
return target;
}
else {
throw 'awfull error';
}
}
}));
...
// Handler for global and uncaugth errors
app.use(function (err, req, res, next) {
if (err === 'awful error') {
res.status(404).send();
}
else {
res.status(500).send();
}
next(err);
});

Express Error Handling Opinion & Best Practice

I have more of a code architecture question about error handling NodeJs Express apps. I am not sure on what is the best pattern for error handling. On that note, what situations should be considered as an error. For instance, is a 401 Unauthorized code considered an error even though this response is expected when sending bad credentials?
When using:
//app.js file
app.use(err, req, res, next){}
I generally tend to only put 5xx errors here which would represent situations in which a database cannot be found or no network connection issue or function failures. As for the rest, I would manually send back a status code, such as a 401, from the controller by explicitly coding res.status(xxx).send(); or something of the sort. But the issue behind what I'm doing is I tend to repeat myself and have to place logging scattered across the app. Is my approach fine? Should I instead be creating multiple error handling middlewares for different ranges of status codes? I need an opnion
I prefer using middleware with your custom error class to deal with this problem.
Let's see a error class, which contains a custom error message, http status code and logLevel if incase you employ logger.
module.exports = class ApiCalError extends Error {
constructor (message, status, logLevel) {
// Calling parent constructor of base Error class.
super(message);
// Capturing stack trace, excluding constructor call from it.
Error.captureStackTrace(this, this.constructor);
// Saving class name in the property of our custom error as a shortcut.
this.name = this.constructor.name;
// You can use any additional properties you want.
// I'm going to use preferred HTTP status for this error types.
// `500` is the default value if not specified.
this.status = status || 400;
this.logLevel = logLevel || 'warn';
}
toResponseJSON () {
return {
success: false,
message: this.message
}
}
};
Now, let's took a look at a controller. We have only sent successful response from this controller, and passed custom errors to middleware.
exports.Login = function(req, res, next) {
const validationResult = validateLoginForm(req.body)
if (!validationResult.success) {
var err = new customError(validationResult.message, 400, 'warn')
return next(err)
} else {
return passport.authenticate('local-login', (err, token, userData) => {
if (err) {
if (err.name == 'IncorrectCredentialsError' || err.name == 'EmailNotVerified') {
var error = new customError(err.message, 400, 'warn')
return next(error)
}
return next(err)
}
return res.json({
success: true,
message: 'You have successfully logged in!',
token,
user: userData
})
})(req, res, next)
}
}
Now, let's take a look at logger and error handlers middlewares. Here, logger will log the errors in api and pass the error to error handlers. These functions would then be used in app.use().
// Import library
var Logger = function(logger) {
return function(err, req, res, next) {
var meta = {
path: req.originalUrl,
method: req.method,
'user-agent': req.headers['user-agent'],
origin: req.headers.origin
}
if (err instanceof customError) {
logger.log(err.logLevel, err.message, meta)
return next(err)
} else {
logger.log('error', err.message, meta)
return next(err)
}
}
}
var ErrorHandler = function() {
return function(err, req, res, next) {
if (err instanceof customError) {
return res.status(err.status).json(err.toResponseJSON())
}else{
return res.status(500).json({
success: false,
message: err.message
})
}
}
}
module.exports = {
Logger,
ErrorHandler
}

Handling errors in nodejs

i am making a nodejs web api and i have a function that returns a user object that is associated with a given authentication token:
module.exports.getByToken = function (token_value, callback)
{
mongoose.model('tokens').findOne({ value: token_value }).exec(function (err, token)
{
if (err || token == null)
{
var error = new Error('couldn\'t find user of the given token');
callback(error, null);
}
else
{
mongoose.model('users').find({ _id: token.user }).exec(callback);
}
});
};
As you can see, i am passing the error back to the callback instead of throwing it. Am i doing it right?
This function is called from an authentication middleware:
app.use('/api', function (req, res, next)
{
var token = req.headers.authorization;
users.getByToken(token, function (err, user)
{
if (err || user == null)
{
res.status(401).end('Unauthorized');
}
else
{
app.locals.user = user;
next();
}
});
});
So the idea of passing the error back to the callback works conveniently.
But is this the right way to handle errors?
Can it block the main thread?
Should i throw the error instead and explicitly catch it in the middleware?
Thanks,
Arik
IMO your are doing it the right way. Callbacks should return an error as the first parameter if they are not responsible for handling it. If you want to improve how any possible error is handled you could change your middleware to something like:
app.use('/api', function (req, res, next){
var token = req.headers.authorization;
users.getByToken(token, function (err, user){
if (err){
res.status(500).end('Something went wrong :('); //token could be valid but you have lost your connection to DB or any other error
}else if (user == null){
res.status(401).end('Unauthorized');
}
else {
app.locals.user = user;
next();
}
});
});
It looks to be right. Nothing wrong in it. I would simplify the code or rather make the middleware separate from the routes in the following manner:
app.use('/api',
auth.checkToken,
auth.processUser //The function that would do something with the user returned from the middleware if there are no errors
);
and in another file (where ever you would want all the middleware related to auth to be, say auth/middleware.js) :
module.exports.getByToken = function (req, res, next)
{ var token_value = req.headers.authorization;
mongoose.model('tokens').findOne({ value: token_value}).exec(function (err, token)
{
if (err)
{
var error = new Error('couldn\'t find user of the given token');
//Log the error, if required
return res.status(500).send()
}
else if(token === null || !token) {
var error = new Error('couldn\'t find user of the given token');
//Log the error, if required
return res.status(404).send(error);
}
else
{ //here next refers to a function that accepts error and user as arguments and does some processing.
mongoose.model('users').find({ _id: token.user }).exec(next);
}
});
};

Resources