Soft deletes — Examples
Soft deletes
Section titled “Soft deletes”Instead of physically deleting a row, stamp a deleted_at and hide it from reads.
A Soft mixin carries the column and a policy that filters
deleted rows out of every SELECT:
abstract type Soft { deleted_at: datetime; policy not_deleted for select using ( .deleted_at is null );}
type Article extends Base, Soft { required title: str;}“Delete” is an update that sets the timestamp:
update Article filter .id = $id<uuid> set { deleted_at := now() };Reads through the app role only ever see live rows — the policy appends
WHERE deleted_at IS NULL for you:
multi select Article { id, title };Counting explicitly (e.g. from a privileged role that bypasses RLS) still works
with an is null filter:
select count(Article filter .deleted_at is null);From generated code
Section titled “From generated code”Save the three queries as soft_delete_article.aql, list_articles.aql, and
count_live_articles.aql:
await runner.query.softDeleteArticle({ id }); // stamps deleted_atconst live = await runner.query.listArticles(); // ListArticlesRow[] — deleted rows hidden by the policyconst n = await runner.query.countLiveArticles(); // number_, err := runner.Query.SoftDeleteArticle(ctx, gen.SoftDeleteArticleParams{ID: id})live, err := runner.Query.ListArticles(ctx) // []ListArticlesRown, err := runner.Query.CountLiveArticles(ctx) // int64