Soft Delete
Was sich ändert
Abschnitt betitelt „Was sich ändert“Wenn softDelete aktiviert ist, versieht ein Löschvorgang eine Spalte mit einem Zeitstempel, anstatt die Zeile zu entfernen,
und jeder Lesevorgang filtert die so markierten Zeilen heraus. Ansonsten ändert
sich an der Operation nichts: Es ist dieselbe Berechtigung erforderlich, beforeDelete
kann weiterhin ein Veto einlegen und afterDelete wird weiterhin ausgelöst. Aus
Sicht des Aufrufers wurde die Zeile gelöscht; wie die Tabelle dies festhält,
ist Aufgabe dieses Flags.
import { defineCollection } from "@rebasepro/cms-types";
const invoices = defineCollection({ slug: "invoices", name: "Invoices", table: "invoices", softDelete: true, properties: { reference: { name: "Reference", type: "string" }, deletedAt: { name: "Deleted at", type: "date", admin: { readOnly: true } } }});true verwendet deletedAt (Spalte deleted_at). Die Objektform benennt sie um:
softDelete: { field: "archivedAt" }.
Nur für Postgres, genau wie search und indexes.
Was bei Lesezugriffen sichtbar ist
Abschnitt betitelt „Was bei Lesezugriffen sichtbar ist“Zeilen mit Zeitstempel werden standardmäßig vor find, findById, count, den
Aggregaten, dem Realtime-Refetch und beim Laden dieser Collection über eine
Relation ausgeblendet. Dieser Standardwert ist genau der Zweck: Code, der vor
der Existenz des Flags geschrieben wurde, funktioniert weiterhin, und niemand
muss daran denken, manuell zu filtern.
Zwei Query-Parameter ermöglichen den Zugriff darauf:
| Parameter | Liefert |
|---|---|
?deleted=include |
Aktive Zeilen und solche mit Zeitstempel |
?deleted=only |
Nur Zeilen mit Zeitstempel – die Papierkorb-Ansicht |
Jeder andere Wert führt zu einem 400-Fehler statt eines stillen Fallbacks.
Würde ?deleted=true stillschweigend alle gelöschten Zeilen ausblenden, würde
es so aussehen, als hätte es funktioniert, und stattdessen die gegenteilige
Frage beantworten.
Wiederherstellen und endgültiges Löschen
Abschnitt betitelt „Wiederherstellen und endgültiges Löschen“Eine Wiederherstellung ist ein ganz normales Update, das das Feld wieder auf
null setzt. Es gibt kein spezielles Verb, da es keinen speziellen Status gibt –
die Zeile war nie weg.
Über REST ist es ein PATCH – einzeln, /bulk oder _batch –, dessen Body das
Feld auf null setzt:
curl -X PATCH /api/data/invoices/7 -d '{"deletedAt": null}'Dieses Update ist dasjenige, das eine gestempelte Zeile erreicht. Jede andere
Bearbeitung davon antwortet mit 404, so wie die Zeile es bei jedem
Standard-Lesezugriff tut; stellen Sie sie zuerst wieder her.
Die Umkehrung ist kein Update. Das Setzen des Feldes auf einen Wert wird mit 400
FIELD_NOT_WRITABLE abgelehnt – löschen Sie die Zeile stattdessen –, sodass die
delete-Berechtigung, beforeDelete und afterDelete immer dafür gelten. Ein
Upsert kann eine Zeile erstellen, die bereits gestempelt ist, stempelt aber nie
eine, die gespeichert war.
Ebenso wenig schreibt ein Upsert in eine Zeile im
Papierkorb: Wenn sein Schlüssel zu einer gestempelten Zeile gehört, wird er mit
409 ROW_IN_TRASH abgelehnt, und nichts wird geschrieben. Stellen Sie die
Zeile zuerst wieder her, oder löschen Sie sie endgültig mit ?hard=true und
führen Sie das Upsert erneut aus. Auf 0.23 schreibt das Upsert seine Werte in
die verborgene Zeile und antwortet mit 201.
Ein Löschvorgang über einen Many-to-Many-Pfad wie DELETE /api/data/posts/1/tags/5
entfernt die Verknüpfung von Post 1 mit dem Tag. Er führt kein Soft Delete des Tags
durch, den andere Posts weiterhin verwenden.
Ein echtes DELETE erfolgt über ?hard=true beim Delete-Aufruf. Es erfordert
exakt dieselbe Berechtigung wie ein normaler Löschvorgang: Es ist dasselbe Verb,
und eine separate Absicherung wäre eine zweite Zugriffskontrollfläche für ein und
dieselbe Operation. Was sich ändert, ist, ob die Zeile wiederhergestellt werden
kann. Nur das Literal true oder 1 bedeutet Ja; ein Tippfehler führt zu einem
400-Fehler, denn ein Aufrufer, der ein vollständiges Löschen angefordert hat und
stattdessen ein Soft Delete erhält, geht davon aus, dass die Daten endgültig gelöscht sind.
Aus dem SDK
Abschnitt betitelt „Aus dem SDK“Der typisierte Client erreicht den Papierkorb über trash an jeder Collection. Er sendet
die oben beschriebenen Anfragen: ?deleted=only bei den Lesezugriffen, das Update auf null für
eine Wiederherstellung und ?hard=true für ein endgültiges Löschen.
const trashed = await client.data.invoices.trash.find({ limit: 20 });const howMany = await client.data.invoices.trash.count();
await client.data.invoices.trash.restore(7); // PATCH { deletedAt: null }await client.data.invoices.trash.restoreMany([8, 9]);
await client.data.invoices.trash.purge(7); // DELETE ?hard=trueawait client.data.invoices.trash.purgeMany([8, 9]);trash.find und trash.count nehmen dieselben Parameter wie find und count.
Eine Wiederherstellung setzt deletedAt, sofern Sie das Feld nicht nennen: Eine Collection, die mit
softDelete: { field: "archivedAt" } deklariert ist, stellt mit
trash.restore(7, { field: "archivedAt" }) wieder her. Der Client kann den Feldnamen
nicht vom Server lesen.
Verwenden Sie trash an einer Collection, die softDelete deklariert. An einer, die es nicht tut,
ignoriert der Server ?deleted=, sodass trash.find die aktiven Zeilen zurückgibt und
trash.purge ein gewöhnliches Löschen ist.
Servercode hat denselben trash: context.data.invoices.trash in einem Callback
und rebase.data.invoices.trash in einer Funktion oder einem Cron. Er liest und löscht endgültig
über den Driver, mit den Berechtigungen des Aufrufers, wie der Rest von
context.data.
Ist Offline aktiviert, wird der Papierkorb nie zwischengespeichert, und nichts
darin wird in die Warteschlange gestellt. Jeder Aufruf braucht den Server und schlägt ohne ihn mit dem
Fehler OFFLINE fehl. Eine wiederhergestellte Zeile kommt in die lokale Datenbank, eine endgültig gelöschte verlässt sie.
Nächste Schritte
Abschnitt betitelt „Nächste Schritte“- Defining Collections — wo
softDeletedeklariert wird - REST API — die Delete- und Abfrage-Endpunkte, zu denen diese Parameter gehören
- Security Rules (RLS) — wer überhaupt eine Zeile löschen darf