ZADorkMan
ZADorkMan

Reputation: 431

Node middleware wait for function to complete before continuing

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

Answers (1)

SuleymanSah
SuleymanSah

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

Related Questions