nohm
Advanced tools
Changelog
3.0.0 (2022-02-28)
Changelog
2.0.0 (2018-06-10)
nohm v0.9 / v1.x supports node versions all the way back to v0.10 and is tested in our CI environments as such.
nohm v2 onwards will restrict this to more current versions of node. At the time of this writing all tests pass on node 8.x, 9.x and 10.x.
For now the CI will stay set up to support 8.x, lts/* (currently 8.x as well) and stable (currently v10). When/if support for 8.x is cancelled, this will be another breaking change and declared as such.
All callbacks have been changed to promises (except events and link optional callbacks).
To migrate your code, replace callback functions with promise handlers.
Old:
instance.load(123, (err, props) => {
console.log(err, props);
});
New:
try {
const props = await modelInstance.load(123);
console.log(props);
} catch (err) {
console.log(err);
}
Sadly the performance of Promises in v8 and node is still not on par with callbacks.
Currently in node v9 nohm@1 is almost twice as fast as nohm@2 at the very simple benchmark in extra/stress.js. In my test nohm@1 does ~21-22k ops/s, while nohm@2 does ~12-13k ops/s.
However node v10 brought great improvements for both: nohm@1 does 28-31k ops/s and nohm@2 does 21-22k ops/s.
The improvements in code readability of Promises versus pure callbacks or caolan/async callbacks makes quite a difference though. As such it seems worth it to switch to Promises and hope for even more performance improvements from v8/node and nohm.
More nohm performance optimizations are planned and any help is very welcome!
Previously, default ids were a custom random implementation, that wasn't very safe. Now uuid v1 is used by default.
Existing default ids should still work with the new versions, but any new default ids created after the update will be uuid v1.
The format for validations was previously based on arrays, and strict indices had to be maintained for different parts of the definition.
Now validation definitions are objects and have proper keys.
Old:
someProperty: {
type: 'integer',
defaultValue: 5,
validations: [
[
'minMax',
{
min: 2,
max: 20
}
]
]
},
New:
someProperty: {
type: 'integer',
defaultValue: 5,
validations: [
{
name: 'minMax',
options: {
min: 2,
max: 20
}
}
]
},
Previously there were cases where an error without a callback would just log out something about it and then continue on.
Now with Promises the behavior will be similar (built-in unhandled rejection log) until Node.js starts to treat unhandled rejection by terminating the process.
Validation failures during save()
previously returned a string 'invalid' and the errors then had to be checked in instance.errors
.
Now validation failures during save()
reject with a ValidationError.
Old:
instance.save((err) => {
if (err) {
if (err === 'invalid') {
console.log('validation failed. reasons:', instance.errors);
} else {
console.error('saving failed with unknown error:', err);
}
} else {
console.log('success!');
}
});
New:
try {
await instance.save();
console.log('success!');
} catch (err) {
if (err instanceof nohm.ValidationError) {
console.log('validation failed. reasons:', err.errors); // or instance.errors still, if you prefer
} else {
console.error('saving failed with unknown error:', err);
}
console.log(err);
}
Previously linking errors had 2 different ways to be handled, controlled via the continue_on_link_error option.
This meant that either all linked objects were attempted to be stored, regardless of a failure in any linked object saving and success callback was called or as soon as one failed, no more links were stored during that save call and an immediate error callback would be issued.
The new behavior is that it always attempts to save all linked objects in series (not parallel), but if any of them fail a rejection is issued at the end of the saving process. The reason it is done in series is that it makes saves more predictable (and thus testable) and it reduces the risk of race-conditions in the link chain.
This makes it abundantly clear that you are in an error state while at the same time allowing for recovery by inspecting the failures and acting accordingly.
A LinkError object is an extension of Error and additionally contains an "errors" array:
linkError.errors ==
[
{
parent: NohmModel, // the instance on which .link() was performed
child: NohmModel, // the instance that was given to .link() as argument
error: Error | ValidationError | LinkError, // the error that occurred while saving child.
},
];
In addition the callbacks that can be provided to the .link() call options object receive different arguments now. The errors array from the linked object is not passed separately as the second argument anymore, instead it is just the thrown Error object as first argument and the linked object instance as second.
Old:
instance.link(other, {
error: (err, errors, linkedInstance) => {
errors === linkedInstance.errors && other === linkedInstance;
},
});
New:
instance.link(other, {
error: (err, linkedInstance) => {
other === linkedInstance;
},
});
model.load()
and instance.load()
now return the same as .allProperties(), meaning the id field is now included in addition to the normal propertiesinstance.valid()
was renamed to instance.validate()
.instance.allProperties()
no longer has a json option (it stringified the return)instance.propertyReset()
no longer returns anything (previously always true)nohm.factory()
was previously only async if an id was provided - now it always returns a Promise, regardless of arguments providedinstance.remove()
now sets instance.id
property to null instead of 0. (this previously potentially caused issues with integer ids starting at 0)nohm.factory()
instead or constructor and instance.load()
afterwards..find()
now throws an error instead of logging an error and returning an empty array.nohm.connect()
is renamed to nohm.middleware()
nohm.middleware()
assumes that you are targeting browsers that have Promise support or that you have a Promise shim..findAndLoad()
now returns an empty array if no instances are found instead of producing an error. This makes the behavior the same in find()
and findAndLoad()
.instance.id
is now a getter/setter that always returns null (no id set) or a stringinstance.isLoaded
is true when the instance has been loaded or saved at least once and instance.id
has not been manually changed.instance.isDirty
is true when anything was done on the model that would require a .save() to persist it (changing .id, properties, pending relation changes)instance.p()
and instance.prop()
have been deprecated. instance.property()
is the only version going forward.Almost the entire code base was rewritten in Typescript.
This means typing your models is now a lot easier. For additional examples of the typing possibilities, see the README.md examples or the Typescript tests.