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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With