Skip to main content

Soft-delete behavior

Query logic

Soft deletable models inherit SoftDeleteBaseModel and have a deleted_at timestamp indicating whether (null or not null) and when they were deleted.

In Objection, they are automatically filtered out on Model.query(), unless you explicitly say otherwise as Model.query(trx, true).

They are also automatically filtered out on

  • Most GraphQL resolvers, since they use .query()
  • Model.relatedQuery(), if the related model is soft-deletable, since this uses .query() of the related model under the hood
  • .withGraphFetched() and .withGraphJoined() queries, since they use .relatedQuery() and thus .query() under the hood

Behavior of deleted items

Accessing a deleted item

To query a deleted item, you need to allow the QueryBuilder to access deleted items:

const deletedProvider = Provider.query(undefined, true).findById(
"c_myDeletedProvider"
);

Once you have the object, you can do whatever you want to it, including updating/patching it.

To permanently delete an item, use .hardDelete().

To undelete an item, use .unDelete() (note capitalization).

Since the soft-delete filter applies to related queries behind the scenes, objects related to a soft-deleted object will act like they don't exist. The relation will return null (although the ID will still be defined):

const test = await Program.query()
.findById("c_test_provider2_program1")
.withGraphJoined("provider");
{
"id": "c_test_provider2_program1",
"deletedAt": null,
...
// The Provider for this Program has been soft-deleted.
// Its ID is still here, but the relation returns null
"providerId": "c_test_provider2",
"provider": null
}

Upstream and downstream implications

You should be mindful of how related objects interact with soft-deleted objects in the business logic. For example, if you soft-delete a Provider, do the still-active Programs and Cohorts still make sense?