How can I pass param from the controller to pre-save middleware? - node.js

My function in the controller:
getWeather: (req, res) => {
const userId = req.params.userId;
weather.save().then(() => {
console.log('weather saved')
}).catch(error => { return res.status(500).json({ error }) })
}
The middleware in the model, here I want to get the userId as a param
weatherSchema.pre('save', function (req, res, next) {
console.log( req + ' pre!! '); //req
next();
})
I don't succeed, I tried to look for similar questions but their answers did not help me. What can I try next?

I guess you're confused between express middleware and mongoose middleware. The save mongoose middleware that you are using is a document middleware and it only gets a single parameter i.e. next and it is triggered before .save().
I guess an express middleware would solve your problem.
app.get("/someroute", (req, res, next) => {
// Here you have access to req.params.userId
next();
}, (req, res) => {
// Here you can save it to the db
})

Related

Error : Cannot set headers after they are sent to the client

When I hit the signup route "req.body" doesn't pick any up any of the POST values, however whenever the same code is tested on Postman - with body raw method - the values display.
const router = require('express').Router();
const bodyParser = require('body-parser');
const Promise = require('bluebird');
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({
extended: true
}));
const registration = require('./services/registration');
router.get('/', (req, res, next) => {
res.send('Admin Welcome');
});
router.get('/signup', (req, res, next) => {
res.render('user/signup');
});
router.post('/signup', (req, res, next) => {
res.send(req.body);
registration.registration(req.body);
.then(ok=>{
res.redirect('signin')
})
.catch(err => {
res.render('error', {message: err})
})
})
router.get('/signin', (req, res, next) => {
res.render('user/signin');
});
original code
router.post("/signup", (req, res, next) => {
res.send(req.body);
registration
.registration(req.body)
.then(ok => {
res.redirect("signin");
})
.catch(err => {
res.render("error", { message: err });
});
});
The res object represents the HTTP response that an Express app sends when it gets an HTTP request. In the following link you can see all the methods that are exposed for res object:
https://expressjs.com/en/api.html#res
Method that you are using at the beginning of you route handler is:
res.send([body])
And as it can be read from the documentation it sends the HTTP response. Now you can send that response only once, otherwise you will get an error:
Error : Cannot set headers after they are sent to the client
And what you are trying to do in the handler is to redirect result to "signin" page afterwards already sending the response with res.send(req.body).
Take this fake route for an example:
router.post("/example", (req, res, next) => {
res.send('1');
res.send('2');
res.send('3');
});
Contrary to what you might believe, it wont return values (1,2,3), but actually return value 1 and raise and error that was previously described.
Finally to solve your issue you need to remove line containing res.send(req.body) and double check if registration.registration service is correctly handling provided data, in this case req.body.

Is it possible to dynamically use a route part to call passport strategy?

Currently, I have the following code for many more oath provider:
// facebook
router.get("/facebook", passport.authenticate("facebook", { scope: ["email"] }));
router.get("/facebook/callback", passport.authenticate("facebook"), (req, res) => {
console.log(chalk.blue("went into facebook callback"));
res.redirect("http://localhost:3000/profile");
});
// github
router.get("/github", passport.authenticate("github"));
router.get("/github/callback", passport.authenticate("github"), (req, res) => {
console.log(chalk.blue("went into github callback"));
res.redirect("http://localhost:3000/profile");
});
Is there a way to unify that into an abstracted route? I.e. something like
// github
router.get("/:provider", passport.authenticate(:provider));
router.get("/:provider/callback", passport.authenticate(:provider), (req, res) => {
console.log(chalk.blue("went into {:provider} callback"));
res.redirect("http://localhost:3000/profile");
});
Update:
The following piece of code does what I want. Thx to #Usman Abdur Rehman.
function callbackDistributer(req, res, next) {
console.log(req.params);
global.provider = req.params.provider;
next();
}
router.get(
"/:provider/callback",
callbackDistributer,
(req, res, next) => {
passport.authenticate(global.provider)(req, res, next);
},
(req, res) => {
console.log(chalk.red("went into: " + global.provider));
res.redirect("http://localhost:3000/profile");
}
);
Have a middleware function going before the passport.authenticate middleware
function ownMiddleware(req,res,next){
global.provider = req.params.provider
next()
}
and then use it in the route handler as
router.get("/:provider/callback", ownMiddleware ,passport.authenticate(global.provider), (req, res) => {
console.log(chalk.blue("went into {:provider} callback"));
res.redirect("http://localhost:3000/profile");
});
I think it should work

prevent express middleware from executing for same parent path

This is my code when.
I am hitting put API it is executing middleware 3 times but it should execute for put API only.
app.use('/api/user', MiddlewareFun);
app.get('/api/user', (req, res) => {
//do something
});
app.use('/api/user', MiddlewareFun);
app.post('/api/user', (req, res) => {
//do something
});
app.use('/api/user', MiddlewareFun);
app.put('/api/user', (req, res) => {
//do something
});
please don't say use like this.
app.put('/api/user', MiddlewareFun, (req, res) => {
//do something
});
Well, it's happening, because you've made it to. If you want the middleware, to be executed at only selected method, you have to specify it. For example:
Instead of doing:
app.use('/api/user', MiddlewareFun)
app.put('/api/user', (req, res) => {
//do something
})
replace use method with put. As you'd bind regular route:
app.put('/api/user', MiddlewareFun)
app.put('/api/user', (req, res) => {
//do something
})
Also, one more thing. You don't have to duplicate your middleware call before every route declaration. If you want to apply a middleware to your whole router, you can use .use() (as you did), or .all(); which will result in the same behavior.
The middlewares in Express are binded to app or to router.
The solution to yuur problem is to check the method of the request object at the middleware
let MiddlewareFun = function (req, res, next) {
if (req.method === 'PUT') {
// do something
}
next()
}
app.use('/api/user', MiddlewareFun);
The answer is, You need to write express middleware which is part of your app or router. You can write as many middlewares you want, but in your case you just need it only once and here is the implementation of that.
const MiddlewareFun = function(req, res, next) {
// req is object which consist of information about request made.
// From req object you can get method name which is called.
if(req.method.toLowerString() === 'put') {
// your logic goes here
}
next();
}
app.use('/api/user', MiddlewareFun);
app.get('/api/user', (req, res) => {
//do something
});
app.post('/api/user', (req, res) => {
//do something
});
app.put('/api/user', (req, res) => {
//do something
});

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));
});
});

Apply to all existing routes?

I have to routes that I currently do like this:
app.all('*', passport.authenticate('facebook-token', { session: false }));
//Here goes specific routes.
app.get('/user/me',
(req, res, next) => {
next();
});
app.get('/user/makeRider',
(req, res, next) => {
req.user.user.makeRider(req.query)
.then((user) => {
next();
});
}
);
app.all('*', (req, res) => {
req.user.user.full().then((fulluser) => {
res.json({
user: fulluser,
params: req.query
});
});
});
They are responsible for authentification and output in my REST-api. The problem with these routes is that they make all routes valid, never throwing 404:s. Is there a better way of doing this, without adding the functions to every route?
This is not a setup that is common to Express (Restify has an option where you can call next() to transfer the request to a specific route, which would be your output handler, but that has its limitations as well).
Here's a possible workaround.
First, declare a output middleware:
let outputMiddleware = (req, res) => {
req.user.user.full().then((fulluser) => {
res.json({
user: fulluser,
params: req.query
});
});
};
Next, store a reference to the Passport middleware:
let authenticateMiddleware = passport.authenticate('facebook-token', { session: false });
And create a wrapper function to chain all middleware functions together:
let chain = (fn) => [ authenticateMiddleware, fn, outputMiddleware ];
Lastly, declare your routes like this:
app.get('/user/me', chain((req, res, next) => {
next();
}));
What this does is basically create route handlers that look like this:
app.get('/user/me', [
passport.authenticate(...),
(req, res, next) => { ...; next() },
(req, res) => { ...; res.json(...) }
]);

Resources