Reputation: 431
I am exporting this function in Node but I need the Booking.find
function to complete before the rest of the code continues.
Original:
module.exports = function() {
return function secured(req, res, next) {
if (req.user) {
const mongoose = require('mongoose');
const Booking = mongoose.model('Booking');
Booking.find((err, docs) => { // I need this to run first
bookingsCount = docs.length;
});
const userProfile = req.user;
res.locals = {
count: bookingsCount, // so that when the page loads this var is available
userProfile: JSON.stringify(userProfile, null, 2),
name: userProfile.displayName,
loggedInEmail: userProfile.emails[0].value,
isAuthenticated: req.isAuthenticated(),
};
return next();
}
req.session.returnTo = req.originalUrl;
res.redirect('/login');
};
};
I tried to create separate functions with a callback but I don't think that worked correct because that would then be an asynchronous approach but I believe I need to make this part synchronous.
Then I tried this below How to wait for the return of a Mongoose search async await and it seems to be returning correctly each time.
Updated:
module.exports = function () {
return async function secured(req, res, next) { // added async
if (req.user) {
const mongoose = require('mongoose');
const Booking = mongoose.model('Booking');
await Booking.find((err, docs) => { // added await
bookingsCount = docs.length;
});
const userProfile = req.user;
res.locals = {
count: bookingsCount,
userProfile: JSON.stringify(userProfile, null, 2),
name: userProfile.displayName,
loggedInEmail: userProfile.emails[0].value,
isAuthenticated: req.isAuthenticated(),
};
return next();
}
req.session.returnTo = req.originalUrl;
res.redirect('/login');
};
};
Is this correct usage of await in such a case and in middelware for each page request and can I safely assume that the page wont load until the Booking.find
promise is resolved?
Attempt 1 as suggested:
module.exports = function () {
return async function secured(req, res, next) {
if (req.user) {
let docs;
try {
docs = await Booking.find((err, docs) => {
const bookingsCount = docs.length;
const userProfile = req.user;
res.locals = {
count: bookingsCount,
userProfile: JSON.stringify(userProfile, null, 2),
name: userProfile.displayName,
loggedInEmail: userProfile.emails[0].value,
isAuthenticated: req.isAuthenticated(),
};
});
return next();
} catch (err) {
console.log(err);
return next(err);
}
}
req.session.returnTo = req.originalUrl;
res.redirect('/login');
};
};
Booking model as requested:
const mongoose = require('mongoose');
const bookingSchema = new mongoose.Schema({
firstName: {
type: String,
required: 'This field is required',
},
lastName: {
type: String,
required: 'This field is required',
},
tourType: {
type: String,
required: 'This field is required',
},
dateBooked: {
type: String,
required: 'This field is required',
},
tourDate: {
type: String,
required: 'This field is required',
},
pax: {
type: String,
required: 'This field is required',
},
phone: {
type: String,
required: 'This field is required',
},
customerEmail: {
type: String,
required: 'This field is required',
},
pickupAddress: {
type: String,
required: 'This field is required',
},
operatorName: {
type: String,
required: 'This field is required',
},
paidStatus: {
type: String,
required: 'This field is required',
},
notes: {
type: String,
},
guidesName: {
type: String,
},
guidesEmail: {
type: String,
},
bookingCreatedSent: {
type: Boolean,
},
calendarEventCreated: {
type: Boolean,
},
clientReminderSent: {
type: Boolean,
},
remindOperators: {
type: Boolean,
},
remindGoCapeGuides: {
type: Boolean,
},
feedbackSent: {
type: Boolean,
},
});
// Custom validation for email
bookingSchema.path('customerEmail').validate((val) => {
emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return emailRegex.test(val);
}, 'Invalid e-mail.');
mongoose.model('Booking', bookingSchema);
DB.js - model is dependent
const mongoose = require('mongoose');
require('dotenv').config();
env = process.env.NODE_ENV;
envString = env;
// mongoDB connection string
const url = process.env['MONGO_DB_URL' + envString];
console.log(url);
mongoose.connect(url, {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false})
.then(() => {
console.log('connected!', process.env.PORT || '8000');
})
.catch((err) => console.log(err));
//db.close();
require('./booking.model');
Use-able attempt:
module.exports = function() {
return async function secured(req, res, next) {
if (req.user) {
const Booking = require('../model/booking.model');
let docs;
try {
docs = await Booking.find((err, docs) => {
const bookingsCount = docs.length;
const userProfile = req.user;
res.locals = {
count: bookingsCount,
userProfile: JSON.stringify(userProfile, null, 2),
name: userProfile.displayName,
loggedInEmail: userProfile.emails[0].value,
isAuthenticated: req.isAuthenticated(),
};
});
return next();
} catch (err) {
console.log(err);
return next(err);
}
}
req.session.returnTo = req.originalUrl;
res.redirect('/login');
};
};
Upvotes: 0
Views: 995
Reputation: 17868
In your updated code, you are both trying to use await and callback.
Also to catch errors in await, we need to use try catch block.
So you can rewrite your function like this:
const mongoose = require("mongoose");
const Booking = mongoose.model("Booking");
module.exports = function() {
return async function secured(req, res, next) {
if (req.user) {
let docs;
try {
docs = await Booking.find();
const bookingsCount = docs.length;
const userProfile = req.user;
res.locals = {
count: bookingsCount,
userProfile: JSON.stringify(userProfile, null, 2),
name: userProfile.displayName,
loggedInEmail: userProfile.emails[0].value,
isAuthenticated: req.isAuthenticated()
};
return next();
} catch (err) {
console.log(err);
return next(err);
}
}
req.session.returnTo = req.originalUrl;
res.redirect("/login");
};
};
And to solve the problem in the original code, you need to move the res.locals related code inside to the Find callback like this so that it only works after Find worked.
module.exports = function() {
return function secured(req, res, next) {
if (req.user) {
const mongoose = require("mongoose");
const Booking = mongoose.model("Booking");
Booking.find((err, docs) => {
bookingsCount = docs.length;
const userProfile = req.user;
res.locals = {
count: bookingsCount,
userProfile: JSON.stringify(userProfile, null, 2),
name: userProfile.displayName,
loggedInEmail: userProfile.emails[0].value,
isAuthenticated: req.isAuthenticated()
};
return next();
});
next();
}
req.session.returnTo = req.originalUrl;
res.redirect("/login");
};
};
UPDATE:
You need to export your model in booking after schema code like this:
module.exports = mongoose.model('Booking', bookingSchema);
And import it like this in your function:
const Booking = require("../models/booking"); //TODO: update your path
Instead of this line:
const Booking = mongoose.model("Booking");
Upvotes: 1