npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

objection-before-and-unique

v0.5.0

Published

Advanced unique validation + Simpler before checks + Final schema validation for Objection.js

Downloads

134

Readme

obau: objection-before-and-unique

Version Build Status Coverage Dependencies Vulnerabilities License Types

Unique validation + Simpler before checks + Final schema validation for Objection.js

Install

npm install objection-before-and-unique

Important notes

Documentation

Docs can be found here. Be sure to check out the complete setup example and caveats first.

Setup

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) {
  // ...
}

Complete Example

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) {
  // ...
}

Caveats

Static update and patch

Because 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:

  • Completely disable updates/patches when we lack the information about the previous record, that is, when doing them via Model.query(). This is the default behavior.
  • Acknowledge that unique validations will fail if any update/patch operation contain any unique field with unchanged data in relation to the already existing database record.

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:

  • As it was mentioned, any update/patch operation containing any unique field with unchanged data for a record will fail.
  • The 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.

Patterns for instance update/patch queries

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 $beforeUpdate

As 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
  }
}