user1177440
user1177440

Reputation:

JavaScript Promises with Mongoose

I am having some difficulty using promises with mongoose for signing up a new user who is not already in the database. Here is my code:

exports.signup = (req) => {
  const newUser = {
    email: req.body.email,
    password: req.body.password
  };
  let result = {};
  return new Promise((resolve, reject) => {
    UserModel.findOne({email: newUser.email}, (err, existingUser) => {
      if (err) {
        return reject(err)
      } else {
        if (existingUser) {
          return reject()
        } else {
          result = newUser;
          UserModel.create(result);
          return resolve(result)
        }
      }
    })
  })
};

The incoming request values are coming in but the app breaks in the finOne collection.

Can anyone spot the error?

Many Thanks.

UPDATE:

if I omit the mongoose method:

exports.signup = (req) => {
  const newUser = {
    email: req.body.email,
    password: req.body.password
  };
  return Promise.resolve(newUser);
};

Everything works, but of course nothing is added to the database. So, as nearly as I can fathom, it is related to my mongoose method.

Final Update:

I figured out what my error was but pandres95 beat me to the punch with the correct answer posted, so I gave him the points. But I will also offer my solution below explaining in detail where I screwed up in case others follwo same path.

Amazing how the folks with less than 10K in points are always the ones to come to the rescue. ;)

Upvotes: 0

Views: 163

Answers (2)

user1177440
user1177440

Reputation:

Solution:

Make the same mistake enough times and eventually you learn. Initially I had this:

import UserModel from '../models/security/user.api.model';

And while correct, my model actually declared the UserModel explicitly as a constant for export, meaning I should have instead:

import { UserModel } from '../models/security/user.api.model';


Although challenging, I am having a ball improving the starter project react-redux-universal-hot-example

I am splitting up my website with a static webpage port and an api port for better performance, and I am using react-redux for state, and isomorphic/universal routing for search engine spiders crawling, which is important because SPA (Single Page Application websites) do not allow search engine crawling automatically.

Unfortunately, or not, the github project aforementioned does not offer a db example, so I had to figure it out on my own.

Here is the sample solution with schema, api actions (server controllers) using JavaScript promises:

user.api.model.js:

'use strict';

import mongoose from 'mongoose';

const UserSchema = new mongoose.Schema({
  email: {type: String, required: '{PATH} is required!', index: {unique: true}},
  password: {type: String, min: 7, max: 15}, required: '{PATH} is required!',
  active: {type: Boolean, default: false},
  .....
  createdAt: { type: Date, default: Date.now() },
  updatedAt: { type: Date, default: Date.now() }
});



export const UserModel = mongoose.model('User', UserSchema);

security.api.actions.js (controller) with promises:

 'use strict';

import { UserModel } from '../../models/user/user.api.model';

.....

exports.signup = (req) => {
  let result = {};
  return new Promise((resolve, reject) => {
    UserModel.findOne({ email: req.body.email}, (err, existingUser) => {
      if (!existingUser) {
        const newUser = new UserModel({
           email: req.body.email,
           password: req.body.password
           ......
          });
        newUser.save((err) => {
            if (err) { 
              reject(err)
            } else {
              result = newUser;
            }
          });
      }
      if (err) reject(err);
      else resolve(result);
    })
  });
};

auth.shared.reducer.js:

const LOAD = 'security/LOAD';
const LOAD_SUCCESS = 'security/LOAD_SUCCESS';
const LOAD_FAIL = 'security/LOAD_FAIL';
const LOGIN = 'security/LOGIN';
const LOGIN_SUCCESS = 'security/LOGIN_SUCCESS';
const LOGIN_FAIL = 'security/LOGIN_FAIL';
const LOGOUT = 'security/LOGOUT';
const LOGOUT_SUCCESS = 'security/LOGOUT_SUCCESS';
const LOGOUT_FAIL = 'security/LOGOUT_FAIL';
const SIGNUP = 'security/SIGNUP';
const SIGNUP_SUCCESS = 'security/SIGNUP_SUCCESS';
const SIGNUP_FAIL = 'security/SIGNUP_FAIL';

const initialState = {
  loaded: false
};

export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case LOAD:
      return {
        ...state,
        loading: true
      };
    case LOAD_SUCCESS:
      return {
        ...state,
        loading: false,
        loaded: true,
        user: action.result
      };
    case LOAD_FAIL:
      return {
        ...state,
        loading: false,
        loaded: false,
        error: action.error
      };
    case LOGIN:
      return {
        ...state,
        loggingIn: true
      };
    case LOGIN_SUCCESS:
      return {
        ...state,
        loggingIn: false,
        user: action.result
      };
    case LOGIN_FAIL:
      return {
        ...state,
        loggingIn: false,
        user: null,
        loginError: action.error
      };
    case LOGOUT:
      return {
        ...state,
        loggingOut: true
      };
    case LOGOUT_SUCCESS:
      return {
        ...state,
        loggingOut: false,
        user: null
      };
    case LOGOUT_FAIL:
      return {
        ...state,
        loggingOut: false,
        logoutError: action.error
      };
    case SIGNUP:
      return {
        ...state,
        signingUp: true
      };
    case SIGNUP_SUCCESS:
      return {
        ...state,
        signingUp: false,
        user: action.result
      };
    case SIGNUP_FAIL:
      return {
        ...state,
        signingUp: false,
        user: null,
        signUpError: action.error
      };
    default:
      return state;
  }
}

export function isLoaded(globalState) {
  return globalState.auth && globalState.auth.loaded;
}

export function load() {
  return {
    types: [LOAD, LOAD_SUCCESS, LOAD_FAIL],
    promise: (client) => client.get('/security/createUserSession')
  };
}

export function login(name) {
  return {
    types: [LOGIN, LOGIN_SUCCESS, LOGIN_FAIL],
    promise: (client) => client.post('/security/login', {
      data: {
        name
      }
    })
  };
}

export function logout() {
  return {
    types: [LOGOUT, LOGOUT_SUCCESS, LOGOUT_FAIL],
    promise: (client) => client.get('/security/logout')
  };
}

export function signup(email, password) {
  return {
    types: [SIGNUP, SIGNUP_SUCCESS, SIGNUP_FAIL],
    promise: (client) => client.post('/security/signup', {
      data: {
        email: email,
        password: password
      }
    })
  };
}

If you are a React coder or enthusiast, I recommend you check out that starter kit if you haven't already. It is bleeding edge, yet I am confident the technology employed will be standard in the not too distant future.

Upvotes: 1

pandres95
pandres95

Reputation: 183

There may be some issues:

  1. I notice you didn't use the reply (i.e. res). Which webserver library/framework are you using?
  2. Then, I would prefer managing control flow by using return as the exit statement, and not a sequence of inline conditionals. They may lead to unexpected results.
  3. Using the result variable can't be recommended in this situation, because:
    1. Being a scope variable, the change of scope can be part of the problem and,
    2. I don't see it necessary, since you use it once.

Here is how I'd solve the same situation from the mongoose schema.

var userSchema = new Schema({
    email: String,
    password: String
});

userSchema.statics.signup = function (data) {
    return new Promise((resolve, reject) => {
        this.findOne({
            email: data.email
        }, (err, existingUser) => {
            if(err) return reject(err);
            if(existingUser) return reject();
            return this.create(data, (err) => {
                if(err) return reject(err);
                return resolve(data);
            });
        });
    });
};

return userSchema;

A complete solution (including some unit tests) can be found here

Upvotes: 1

Related Questions