3.0.5 • Published 8 months ago

objection-js-soft-delete v3.0.5

Weekly downloads
138
License
MIT
Repository
github
Last release
8 months ago

Build Status NPM

objection-js-soft-delete

A plugin that adds soft-delete functionality to Objection.js

Installation

NPM

npm i objection-js-soft-delete

Yarn

yarn add objection-js-soft-delete

Register the plugin

The mixin provides the following configuration to override the default options:

columnName: the column name that indicate if the record is deleted. The column must exist on the table for the model. Default: 'deleted_at'

deletedValue: define which value indicate if the record is deleted. Default: new Date() (local time zone of the server)

It's also possible to use the time from the database: knex.fn.now()

notDeletedValue: define which value indicate if the record is not deleted. You can set (and should) this option along with deletedValue. Default: NULL

// Import objection and the plugin.
import { Model } from 'objection';
import objectionSoftDelete from 'objection-js-soft-delete';

// Specify the options for this plugin. This are the defaults.
const softDelete = objectionSoftDelete({
    columnName: 'deleted_at',
    deletedValue: new Date(),
    notDeletedValue: null,
});

// Inject the plugin to the model
class User extends softDelete(Model) {
  static get tableName() {
    return 'Users';
  }
}

Note: A deletedValue value of NULL will result in this plugin an unexpected behavior.

Usage

  • Soft delete records: When an record will be deleted then the deleted field is set to the value that is specified as deletedValue:
await User.query().where('id', 1).delete();
await User.query().deleteById(1);

// Deleted rows are still in the db:
// User { id: 1, deleted_at: 'Sat Oct 31 2020 15:42:29 GMT+0100 (Central European Standard Time)' , ... }

Methods

  • .whereNotDeleted(): Returns only records that aren't deleted.
const notDeletedUsers = await User.query().whereNotDeleted();
  • .whereDeleted(): Returns only records that are deleted.
const deletedUsers = await User.query().whereDeleted();
  • .undelete(): Restore deleted records.
await User.query().where('id', 1).undelete();
// User { id: 1, deleted_at: null }
  • .hardDelete(): Permanently delete records.
await User.query.where('id', 1).hardDelete();

Filters

A notDeleted and a deleted filter will be added to the list of named filters for any model that use this mixin. Internally they are using the methods .whereNotDeleted() and .whereDeleted() from above:

  • notDeleted: Returns only records from the relation that are not deleted.
// withGraphFetched
const rows = await User.query().withGraphFetched('contact(notDeleted)');
// withGraphJoined
const rows = await User.query().withGraphJoined('contact(notDeleted)');
  • deleted: Returns only records from the relation that are deleted.
// withGraphFetched
const rows = await User.query().withGraphFetched('contact(deleted)');
// withGraphJoined
const rows = await User.query().withGraphJoined('contact(deleted)');
  • Relationship filter: A filter can be applied also directly to a relationship:
class User extends Model {
  static get tableName() {
    return 'user';
  }

  static get relationMappings() {
      const Contact = require('./Contact');

      return {
          contact: {
            relation: Model.ManyToManyRelation,
            modelClass: Contact,
            join: {
                from: 'user.id',
                through: {
                    from: 'user_contact.user_id',
                    to: 'user_contact.contact_id',
                },
                to: 'contact.id',
            },
            // .whereNotDeleted() or .whereDeleted()
            filter: (f) => f.whereNotDeleted(),
          },
      };
  }
}

then:

// Will return only records from the relationship contact that are not deleted
await User.query().withGraphFetched('contact');

Using with upsertGraph

This plugin was actually born out of a need to have .upsertGraph() soft delete in some tables, and hard delete in others.

// a model with soft delete
class Phone extends softDelete(Model) {
  static get tableName() {
    return 'Phones';
  }
}

// a model without soft delete
class Email extends Model {
  static get tableName() {
    return 'Emails';
  }
}

// assume a User model that relates to both, and the following existing data:
User {
  id: 1,
  name: 'Johnny Cash',
  phones: [
    {
      id: 6,
      number: '+19195551234',
    },
  ],
  emails: [
    {
      id: 3,
      address: 'mib@americanrecords.com',
    },
  ]
}

await User.query().upsertGraph({
  id: 1,
  name: 'Johnny Cash',
  phones: [],
  emails: [],
});
// => phone id 6 will be flagged deleted (and will still be related to Johnny!), email id 3 will be removed from the database

Lifecycle Functions

One issue that comes with doing soft deletes is that your calls to .delete() will actually trigger lifecycle functions for .update(), which may not be expected or desired. To help address this, some context flags have been added to the queryContext that is passed into lifecycle functions to help discern whether the event that triggered (e.g.) $beforeUpdate was a true update, a soft delete, or an undelete:

$beforeUpdate(opt, queryContext) {
  if (queryContext.softDelete) {
    // do something before a soft delete, possibly including calling your $beforeDelete function.
    // Think this through carefully if you are using additional plugins, as their lifecycle
    // functions may execute before this one depending on how you have set up your inheritance chain!
  } else if (queryContext.undelete) {
    // do something before an undelete
  } else {
    // do something before a normal update
  }
}
  
// same procedure for $afterUpdate

Available flags are:

  • softDelete
  • undelete

Flags will be true if set, and undefined otherwise.

Checking Whether a Model is SoftDelete

All models with the soft delete mixin will have an isSoftDelete property, which returns true.

const modelHasSoftDelete = User.isSoftDelete;

Tests

Tests can be run with:

npm test

or:

yarn test

Linting

The linter can be run with:

npm run lint

or:

yarn lint
4.0.0-beta.0

8 months ago

3.0.4

1 year ago

3.0.5

1 year ago

3.0.3

2 years ago

3.0.2

2 years ago

3.0.1

2 years ago

3.0.0

2 years ago

1.1.5

3 years ago

1.1.4

3 years ago

1.1.3

3 years ago

1.1.2

3 years ago

1.1.1

3 years ago

1.0.10

3 years ago

1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago