Bookshelf Paranoia

0.13.1 · maintenance · verified Wed Apr 22

bookshelf-paranoia is a plugin for Bookshelf.js that provides a transparent soft-delete mechanism for database records. Instead of permanently removing rows when `destroy` is called on a model, it sets a `deleted_at` timestamp on the record, effectively marking it as deleted without losing the data. This allows for easier data recovery and maintains historical data integrity within the database. The package is currently at version 0.13.1. A crucial aspect of this package is its "unmaintained" status, as explicitly stated by the author, who only dedicates minimal time to small fixes at a slow pace. This implies an uncertain release cadence and potential for slow resolution of issues. Its primary differentiator lies in seamlessly integrating soft-delete logic directly into Bookshelf models and queries, automatically excluding soft-deleted records from standard `fetch` operations and eager loadings, while offering overrides for hard deletion or retrieval of deleted records. This transparent approach minimizes changes required in application logic when implementing soft deletes.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates the installation, model configuration, and basic usage of `bookshelf-paranoia` including soft deletion, fetching deleted records, and performing hard deletes.

const Knex = require('knex');
const Bookshelf = require('bookshelf');

// Mock Knex setup for demonstration
const knex = Knex({
  client: 'sqlite3',
  connection: { filename: ':memory:' },
  useNullAsDefault: true
});

const bookshelf = Bookshelf(knex);

// Add the plugin
bookshelf.plugin(require('bookshelf-paranoia'));

// Define a model with soft delete enabled
const User = bookshelf.Model.extend({
  tableName: 'users',
  softDelete: true,
  idAttribute: 'id'
});

async function runExample() {
  try {
    // Create users table if it doesn't exist
    await knex.schema.hasTable('users').then(exists => {
      if (!exists) {
        return knex.schema.createTable('users', table => {
          table.increments('id').primary();
          table.string('name');
          table.timestamp('deleted_at'); // Default field for soft delete
        });
      }
    });

    // Insert a user
    const newUser = await User.forge({ name: 'Alice' }).save();
    console.log('Created user:', newUser.toJSON());

    // Soft delete the user
    await newUser.destroy();
    console.log('Soft-deleted user.');

    // Try to fetch the user (should return null as it's soft-deleted)
    const fetchedUser = await User.forge({ id: newUser.id }).fetch();
    console.log('Fetched user after soft-delete (should be null):', fetchedUser ? fetchedUser.toJSON() : 'null');

    // Fetch the user, including soft-deleted records
    const fetchedWithDeleted = await User.forge({ id: newUser.id }).fetch({ withDeleted: true });
    console.log('Fetched user withDeleted: ', fetchedWithDeleted.toJSON());
    console.log('Deleted at timestamp:', fetchedWithDeleted.get('deleted_at'));

    // Perform a hard delete (bypassing soft delete)
    const userToHardDelete = await User.forge({ name: 'Bob' }).save();
    console.log('Created user for hard delete:', userToHardDelete.toJSON());
    await userToHardDelete.destroy({ hardDelete: true });
    console.log('Hard-deleted user.');
    const fetchedHardDeleted = await User.forge({ id: userToHardDelete.id }).fetch({ withDeleted: true });
    console.log('Fetched hard-deleted user (should be null):', fetchedHardDeleted ? fetchedHardDeleted.toJSON() : 'null');

  } catch (error) {
    console.error('Error during example run:', error);
  } finally {
    await knex.destroy();
  }
}

runExample();

view raw JSON →