Home > Enterprise >  Objects instances retrieved by findOne do not have any methods
Objects instances retrieved by findOne do not have any methods

Time:06-13

Whenever I retrieve an object from my mongo database, it doesn’t have any methods and I am trying to figure out (1) why and (2) a solution.

To summarise, the following sums up the problem:

(1) I can create an object and its methods work:

const newUser = new User(email, hashedPassword);
console.log(newUser.test); // correct - it’s not undefined

(2) I can insert the instance (with its methods) into the database (via saveToDb):

const insertedUser = await collection.insertOne(this);

(3) I can retrieve it from the database (via findByEmail):

const user = await collection.findOne({ email });

(4) But it doesn’t have any methods anymore:

if (!user) return; // temporary code
console.log(user); // correctly displays object keys and values
console.log('user.test', user.test); // undefined - why?

Why does this happen? I’ve read a few other posts about it, but they all seem to use mongoose, which I do not use in my app (maybe I should use it?). This post did not get an answer, which I think is a similar issue.

Any insight would be appreciated, thank you.

Incase needed to see, here’s the class:

export class User {
  email: string;
  hashedPassword: any;
  dateCreated: Date;

  constructor(email: string, hashedPassword: any) {
    this.email = email; // make email.toLowerCase(); 
    this.hashedPassword = hashedPassword; 
    this.dateCreated = new Date();
  }

  async saveToDb() {
    try {
      const collection = getCollection(USERS_COLLECTION_NAME);
      const sanitisedEmail = this.email.toLowerCase();
      const insertedUser = await collection.insertOne(this);

      console.log('THIS HAS BEEN INSERTED TO DB:', this);
      console.log('this.test:', this.test); // works
      this.test();

      const token = jwt.sign(insertedUser, sanitisedEmail, {
        expiresIn: 60 * 24,
      });
      return token;
    } catch (err) {
      throw err;
    }
  }

  test() {
    console.log('test() within class Fn');
    return 5;
  }

  static staticTest() {
    console.log('staticTest() within class Fn');
    return 6;
  }

  signToken() {
    const token = jwt.sign(this, this.email, {
      expiresIn: 60 * 24,
    });
    return token;
  }

  static async fetchAll() {
    try {
      const collection = getCollection(USERS_COLLECTION_NAME);
      const users = await collection.find().toArray();  
      return users;
    } catch (err) {
      throw err;
    }
  }

  static async findByEmail(email: string) {
    try {
      const collection = getCollection(USERS_COLLECTION_NAME);
      const user = await collection.findOne({ email });
      if (!user) return; // temporary code
      console.log('FOUND THIS USER: ', user); // works
      console.log('user.test', user.test); // undefined - key problem lies here...
      return user;
    } catch (err) {
      throw err;
    }
  }
}

CodePudding user response:

The objects you get back via query methods, such as findOne, will be plain objects. They are not instances of your class, as these objects were sent and saved as JSON in the database, and this does not include class information, prototypes, nor methods.

So what you can do, is change the prototype of the object you get back. Or better, create a new instance of your class with Object.create and inject the properties with Object.assign:

const user = Object.assign(
    Object.create(User.prototype), 
    await collection.findOne({ email })
);

Now your user object will again have access to the prototype methods.

For completeness sake, I'll also mention the alternative:

const user = await collection.findOne({ email });
Object.setPrototypeOf(user, User.prototype);

But read the disclaimer provided by Mozilla Contributors on setPrototypeOf:

Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, currently a very slow operation in every browser and JavaScript engine. In addition, the effects of altering inheritance are subtle and far-flung, and are not limited to the time spent in the Object.setPrototypeOf(...) statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered.

  • Related