skip to Main Content

I’ll be grateful if you can help me understand an error generated by mongoose.

Log Error:

TypeError: Cannot read properties of undefined (reading 'name')
    at Object.isPOJO (C:Users[...]node_modulesmongooselibutils.js:396:38)
    at Object.toObject (C:[...]node_modulesmongooselibutils.js:354:15)
    at model.Query.Query.find (C:[...]node_modulesmongooselibquery.js:2341:22)
    at Function.find (C:[...]node_modulesmongooselibmodel.js:2244:13)
    at Object.exports.findMatches (C:[...]controllersmatches.js:13:11)
    at preHandlerCallback (C:[...]node_modulesfastifylibhandleRequest.js:128:37)
    at preValidationCallback (C:[...]node_modulesfastifylibhandleRequest.js:112:5)
    at handler (C:[...]node_modulesfastifylibhandleRequest.js:76:7)
    at handleRequest (C:[...]node_modulesfastifylibhandleRequest.js:24:5)
    at runPreParsing (C:[...]node_modulesfastifylibroute.js:522:5)

The point is I wasn’t working on anything related Mongoose. I’ve just realized it’s happening (all of a sudden, the code was working fine a while ago) only when I use dates in my query (query sample: {startDate: { $gt: new Date(req.query.date)} }: req.query.date is a ISO string date). Now I get the above error in every query regarding dates, while they were working fine.

Mongoose code generating the error is the following:

exports.isPOJO = function isPOJO(arg) {
    if (arg == null || typeof arg !== 'object') {
      return false;
    }
    const proto = Object.getPrototypeOf(arg);
    // Prototype may be null if you used `Object.create(null)`
    // Checking `proto`'s constructor is safe because `getPrototypeOf()`
    // explicitly crosses the boundary from object data to object metadata
    return !proto || proto.constructor.name === 'Object';
  };

As a test, if I change proto.constructor.name to proto.constructor?.name resolves the error and everything works fine, but the goal is to understand what’s going on, not messing up with a dependency code.
If it’s relevant, I’m using Fastify in this application.

2

Answers


  1. Chosen as BEST ANSWER

    It appears the issue was relative to query string parsing. Solved by passing explicitly the querystringParser parameter to Fastify:

    const querystring = require('querystring'); 
    const fastify = require('fastify')({       
       querystringParser: str => querystring.parse(str) 
    });
    

    Note: querystringParser is usually used to change the default parser or changing the default behaviour. I'm not sure why in my istance I'm needing to set explicitally.

    Official Fastify documentation about querystringParser:

    querystringParser

    The default query string parser that Fastify uses is the Node.js's core querystring module.

    You can change this default setting by passing the option querystringParser and use a custom one, such as qs.

    const qs = require('qs') 
    const fastify = require('fastify')({  
       querystringParser: str => qs.parse(str) 
    })
    

    You can also use Fastify's default parser but change some handling behaviour, like the example below for case insensitive keys and values:

    const querystring = require('querystring') 
    const fastify = require('fastify')({   
       querystringParser: str => querystring.parse(str.toLowerCase()) 
    })
    

    Note, if you only want the keys (and not the values) to be case insensitive we recommend using a custom parser to convert only the keys to lowercase.

    Thanks everyone for answers!


  2. The mongoose isPOJO code doesn’t work for objects that don’t inherit the Object prototype.

    > a = Object.create(null)
    [Object: null prototype] {}
    
    > typeof a
    'object'
    
    > a.constructor.name
    Uncaught TypeError: Cannot read properties of undefined (reading 'name')
    

    As you mentioned query strings, querystring.parse() returns objects without the Object prototype so those default Object properties can’t clash with incoming query parameters.

    Maybe raise the issue in mongoose if you think this use is valid.

    A work around is to reconstruct the object being passed through if you are not concerned about request params with the prototype names:

    a.__proto__             a.constructor           
    a.hasOwnProperty        a.isPrototypeOf
    a.propertyIsEnumerable  a.toLocaleString
    a.toString              a.valueOf
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search