
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
objection-before-and-unique
Advanced tools
Advanced unique validation + Simpler before checks + Final schema validation for Objection.js
Unique validation + Simpler before checks + Final schema validation for Objection.js
npm install objection-before-and-unique
Model.query() update and patch operations are disabled. You must use instance.$query() for any update or patch operation. You can check here how to do it instead, and review static update and patch for more information.$beforeUpdate and $beforeInsert to run all checks. If you want to use them in your model, you should always call super.$beforeInsert or super.$beforeUpdate first. Here's how.Docs can be found here. Be sure to check out the complete setup example and caveats first.
To set up, mixin the model:
import { Model } from 'objection';
import obau from 'objection-before-and-unique';
// pass options when mixin in the model
const opts = {
// ...
};
class MyModel extends obau(opts)(Model) {
// ...
}
import { Model } from 'objection';
import obau from 'objection-before-and-unique';
const opts = {
schema: {
type: 'object',
required: ['hash'],
properties: {
hash: { type: 'string' }
}
},
unique: [
{ key: 'username' },
{ key: 'email' },
{ key: ['alias', 'team_id'] }
],
before: [
({ instance, old, operation, context }) => {
// Maybe mutate the object like so
if (operation !== 'insert' && old.someProperty) {
instance.someProperty += old.someProperty;
} else if (context.toAdd) {
instance.someProperty += context.toAdd;
}
},
async ({ instance }) => {
// Or mutate the object asynchronously like so
instance.hash = await someAsyncHashFunction(instance.password);
delete instance.password;
},
async ({ instance }) => {
// Do some async checks
// Throw if it fails
if (await someValidationFails(instance)) {
throw Error('Some Error');
}
},
({ instance }) => {
// Maybe some additional sync checks
// Throw with a Model ValidationError
if (someValidationFails(instance)) {
throw Model.createValidationError({
someKey: [{
message: 'Some message',
keyword: 'unique'
}]
});
}
}
]
};
class MyModel extends obau(opts)(Model) {
// ...
}
update and patchBecause of the way Objection.js works, as it doesn't recover and pass the previous instance when doing patches or updates through Model.query() to $beforeUpdate, any update/patch call to the static Model.query() method will lack any information regarding the database record to update/patch. The implication of this being that, when validating the uniqueness of any value, if any of the values to update/patch is equal to the previous value in the database for that record, we won't know that it is actually the same record and therefore the validation will fail.
Within this limitations, there are two available options:
Model.query(). This is the default behavior.With this module, Model.query() patches and updates are disabled by default -you can check how to do it instead below. However, this default behavior can be disabled by setting the old option key to false when calling the default export of this module, as long as you're aware that, when doing so:
old key will never be passed to before hooks or unique callbacks, to ensure consistency and prevent mistakes.It would be, for example, fitting to disable the default behavior when there are no checks for uniqueness and you don't require any information from the previous values on your before hooks.
For single queries, you could follow these straightforward patterns when you lack the instance to update/patch:
Update:
MyModel
.query()
.first()
.where('column', 'value')
// Throw if no result found for the select query.
.throwIfNotFound()
.then((m) => m.$query().updateAndFetch({ myColToChange: 'myNewValue' }));
Patch:
MyModel
.query()
.first()
.where('column', 'value')
// Return without patching if there is no result.
// The check would be 'm.length < 1' if we were
// expecting an array instead of a single object.
.then(
(m) => (!m ? m : m.$query().patchAndFetch({ myColToChange: 'myNewValue' }))
);
$beforeInsert and $beforeUpdateAs this plugin uses $beforeInsert and $beforeUpdate under the hood, if you decide to use them instead of or in addition to before checks, make sure to always call and resolve the super of the function like so:
import { Model } from 'objection';
import obau from 'objection-before-and-unique';
class MyModel extends obau(opts)(Model) {
async $beforeInsert(context) {
await super.$beforeInsert(context);
// Your $beforeInsert checks
}
async $beforeUpdate(options, context) {
await super.$beforeUpdate(options, context);
// Your $beforeUpdate checks
}
}
FAQs
Advanced unique validation + Simpler before checks + Final schema validation for Objection.js
We found that objection-before-and-unique demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.