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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@payload-pln/soft-delete

v1.0.6

Published

`soft-delete` is a reusable [Payload CMS](https://payloadcms.com) plugin that blocks native permanent deletion on selected collections and adds an audited, admin-friendly "Soft delete" flow instead.

Readme

Payload Soft Delete

soft-delete is a reusable Payload CMS plugin that blocks native permanent deletion on selected collections and adds an audited, admin-friendly "Soft delete" flow instead.

  • trash: false — Payload's native trash feature is never involved.
  • Native delete is blocked from the Admin UI, REST, GraphQL and the Local API via access.delete: () => false plus a beforeDelete hook guard. No permanent deletion is possible through normal admin or API operations—even with overrideAccess.

Install and configure

import { buildConfig } from 'payload'
import { softDelete } from '@payload-pln/soft-delete'

export default buildConfig({
  collections: [Posts, Users],
  plugins: [
    softDelete({
      collections: {
        users: true,
        posts: true,
      },
    }),
  ],
})

What the plugin does

For every collection listed in collections, the plugin:

  1. Sets trash: false and access.delete: () => false, so native Payload delete is impossible (Admin UI button is gone, REST/GraphQL/Local API throw 403).
  2. Adds three audit fields:

| Field | Type | Purpose | | ---------------- | -------- | ---------------------------------------------- | | isSoftDeleted | checkbox | true once the document has been soft-deleted | | softDeletedAt | date | When it was soft-deleted | | softDeletedBy | text | ID of the user who soft-deleted it (req.user) |

All three are hidden from the admin edit screen and are only ever set through the plugin's soft-delete path. 3. Adds a Soft delete column to the admin list view. Each row's button calls the collection endpoint and then refreshes the list. 4. Adds a collection endpoint POST /api/<collection>/:id/soft-delete that performs a plain payload.update():

Payload Delete         →  BLOCKED (403)
Admin List View        →  Soft delete button
                          →  POST /:id/soft-delete
                          →  payload.update()
                          →  isSoftDeleted = true
                              softDeletedAt = now
                              softDeletedBy = current user

The soft-delete update touches only the three audit fields; documents are never removed from the database.

A beforeChange hook keeps the audit fields consistent no matter which API path flips isSoftDeleted to true (the endpoint, a REST PATCH, or the Local API).

API

// Soft delete a single document (requires update access on the collection)
await fetch('/api/posts/66f1.../soft-delete', { method: 'POST' })

// Local API equivalent
await payload.update({
  collection: 'posts',
  id: postId,
  data: {
    isSoftDeleted: true,
    softDeletedAt: new Date().toISOString(),
    softDeletedBy: req.user?.id ? String(req.user.id) : null,
  },
})

// Native delete is blocked everywhere
await payload.delete({ collection: 'posts', id: postId, overrideAccess: false }) // throws

// Soft-deleted documents stay fully readable — this plugin deliberately does not add
// read filtering, restore, GDPR or trash views yet.

Tests

  • pnpm test:int verifies the default field values, the audited soft-delete update, the blocked native delete (Local API), the REST endpoint and the blocked REST delete.
  • pnpm test:e2e drives the admin UI: clicking Soft delete in the list view marks the row, and the native Delete action is unavailable.