This document serves as a corrolary to this issue on aep.dev, including my thoughts to remove the soft delete pattern.

The background #

As of 2025-09-13, the aeps have a pattern in which one can “soft delete” a resource - where it not actually removed from a collection, but is actually flagged as deleted. This allows the resource to be recoverable later.

The soft delete pattern, I believe, exists to serve the purpose of situations where recovery is desired, such as most situations where persistent data is stored:

  • A database, where recreating the resource does not restore any valuble data stored.
  • A filesystem, where a file may need to be recovered after the fact.

The implementation of soft delete today #

The current implementation of AEPs’ soft delete boils down to the following:

  1. a new custom method, undelete, on the resource.
  2. an added field show_deleted to the List standard method, where resources are hidden unless specified.
  3. an added field show_deleted to the Get standard method, where the resource will return 410 without it.

There are a few other details (such as the usage of expiry_date) that are additional, but do not force any additional fields on the standard methods.

The problems: soft delete is not compatible with declarative clients #

Declarative clients expect consistent resource-oriented operations on each resource - for simple CRUD on a resource, declarative clients are able to easily map each operation to the resource lifecycle.

graph LR
  exists
  not_exists
  exists -- "update" --> exists
  not_exists -- "create" --> exists
  exists -- "delete" --> not_exists

For soft delete, where would that fit in? Effectively it creates another state - soft deleted.

graph LR
  exists
  soft_deleted
  not_exists
  not_exists -- "create" --> exists
  exists -- "update" --> exists
  soft_delete -- "undelete" --> exists
  soft_delete -- "expiry hit" --> not_exists

But this may also result in weird cases like:

A resource “create” throwing an error because the resource is soft-deleted, and therefore exists. But a get would return a 404.

Proposed alternative #

Since the primary use case is about backing up and enabling restoration of data, the proposal would be to replace these augmentations on the primary resource, to adding a second resource for “backups” or “snapshots”.

The pattern would look something like:

/database/foo
/database-snapshots/foo/snapshots/latest

The database-snapshot could be support a custom method, restore, that would be similar to undelete and bring an old resource back.

This would be similar to the resource revision pattern - where a design pattern is introduced by adding a new resource, not by augmenting standard methods.