Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequelize findOne returned instance. Row is in dataValues, but direct properties on instance are undefined

Context: I'm testing a simple model into and out of the database. Not a real test, it's a precursor to some integration tests. Using Sequelize and findOne.

The problem: The direct data on the returned model instance, i.e. email.ulid, email.address, email.isVerified are undefined.

My question: Why are they undefined?

Sequelize: 5.15.0
Typescript: 3.5.3
mysql  Ver 15.1 Distrib 10.4.6-MariaDB

Record into database:

await testingDatabase.sync({
  force: true
}).then(async () => {
  await Email.create({
    ulid: the_ulid.bytes,
    address: "[email protected]",
    isVerified: false
  })
})

Fetch the record back:

await Email.findOne({
  where: { 
    address: "[email protected]"
  }
  , rejectOnEmpty: true //to shut typescript up
}).then( emailData => {
   console.log("Email: ", email)
})

Console.log of email instance:

    Email:  Email {                                                                                                                                                      
      dataValues: {                                                                                                                                                      
        ulid: <Buffer 01 6c a3 36 11 9c 1e 9b a6 ce 60 b7 33 3e e7 21>,                                                                                                  
        address: '[email protected]',                                                                                                                                    
        isVerified: false,                                                                                                                                               
        deletedAt: null,                                                                                                                                                 
        createdAt: 2019-08-18T05:32:05.000Z,                                                                                                                             
        updatedAt: 2019-08-18T05:32:05.000Z                                                                                                                              
      },                                                                                                                                                                 
      ...,
      isNewRecord: false,
      ulid: undefined,
      address: undefined,
      isVerified: undefined,
      createdAt: undefined,
      updatedAt: undefined,
      deletedAt: undefined
    }

^^^ As is clear, immediately above, all of the direct attributes on the instance are null.

The following works, but with the side-effect that isVerified is returned as a 0 rather than false and subsequently fails a direct comparison with the original data. Additionally, I lose other functionality of the model instance that will come in handy on more complex models:

Email.findOne({
  where: { address: "[email protected]" }
, raw: true 
, rejectOnEmpty: true
})

These also work, but with the consequence that Typescript complains about the returned object not having the properties I then access (although they do exist and the test works):

.then( emailData => {
  console.log("All getters: ", emailData.get())
  // AND
  console.log("All getters(plain): ", emailData.get({plain: true}))
  // AND
  console.log("toJSON: ", emailData.toJSON())
})

This next one (suggested by Vivek), works insofar as the data is available, but JSON.parse fails to handle the Buffer properly:

const dataOnly = JSON.parse(JSON.stringify(emailData))

Others have complained (SE answer) that console.log somehow misprints the instance, yet every other way I access these properties they are still undefined.

like image 952
Paul Parker Avatar asked Aug 18 '19 06:08

Paul Parker


2 Answers

I hit the same issue but in a slightly different context.

My solution was not to use the ESNext target in my tsconfig.json. Using ES2021 or any other target solved the problem.

like image 78
pihentagy Avatar answered Oct 09 '22 18:10

pihentagy


Solution:

The problem was with a Babel plugin.

Setting up Sequelize models as per the Sequelize/Typescript documentation, defines class properties on each model representing model fields.

The babel plugin:

"@babel/proposal-class-properties"

within:

plugins: [
  "@babel/proposal-class-properties"
, ...
]

Transforms these class properties into defineProperty() calls on the model Class object, called in turn by the helper _defineProperty():

  _defineProperty(this, "yourFieldName", void 0);

This initializes the property on the object with the value undefined.

Evidently, when findOne subsequently tried to update the model, it refused to overwrite these existing properties on the model.

like image 39
Paul Parker Avatar answered Oct 09 '22 18:10

Paul Parker