---
term: 'Datenbank-Trigger (BeforeSave und AfterSave)'
seoTitle: 'Datenbank-Trigger: beforeSave- und afterSave-Hooks erklärt'
headline: 'Was sind Datenbank-Trigger (BeforeSave und AfterSave)?'
slug: datenbank-trigger
category: backend-compute
shortDefinition: 'Ein Datenbank-Trigger ist ein Code-Hook, der bei Datenereignissen automatisch läuft – vor dem Speichern zum Validieren, danach zum Reagieren.'
relatedTerms:
  - cloud-code-serverless-functions
  - webhooks
  - crud-operations
  - real-time-live-queries
contrastsWith:
  - webhooks
aboutTerms:
  - 'beforeSave'
  - 'afterSave'
  - 'BEFORE/AFTER Triggers'
faq:
  - question: 'Was ist ein Datenbank-Trigger?'
    answer: 'Prozeduraler Code, der automatisch ausgeführt wird, sobald ein Datenereignis – Insert, Update, Delete – auf einer bestimmten Tabelle oder Klasse eintritt. Die klassische Form lebt in der SQL-Datenbank; die moderne Form auf Anwendungsebene ist eine Hook-Funktion wie beforeSave oder afterSave, die das Backend um jeden Schreibvorgang herum ausführt.'
  - question: 'Was ist der Unterschied zwischen BEFORE- und AFTER-Triggern?'
    answer: 'Zeitpunkt und Befugnisse. BEFORE läuft vor dem Schreibvorgang und kann die eingehenden Daten validieren, verändern oder die Operation ganz abbrechen. AFTER läuft, nachdem der Schreibvorgang erfolgreich war – es kann nichts mehr ändern, nur noch reagieren: protokollieren, zählen, benachrichtigen. AFTER feuert nie für Schreibvorgänge, die fehlgeschlagen sind.'
  - question: 'Was ist der Unterschied zwischen einem Trigger und einer Stored Procedure?'
    answer: 'Der Aufruf. Eine Stored Procedure wird explizit aufgerufen, nimmt Parameter entgegen und liefert Ergebnisse zurück. Ein Trigger wird nie aufgerufen – er feuert automatisch, wenn sein Ereignis eintritt, ohne Parameter, an eine Tabelle gebunden. Dieselbe prozedurale Maschinerie, das umgekehrte Aktivierungsmodell.'
  - question: 'Gehört Logik in Trigger oder in den Anwendungscode?'
    answer: 'Der Konsens ist hybrid: Trigger (oder Hooks) für Regeln, die auf jedem Schreibpfad gelten müssen – Validierung, Integrität, Audit – und Anwendungsdienste für komplexe Abläufe. Hooks auf Anwendungsebene sind der moderne Mittelweg: die Garantien eines Triggers, eine echte Programmiersprache, Versionskontrolle.'
  - question: 'Beeinträchtigen Trigger die Performance?'
    answer: 'Sie laufen synchron im Schreibpfad, also verlangsamt ein langsamer Trigger jeden Speichervorgang, und Trigger auf Zeilenebene vervielfachen sich bei Massenoperationen – ein Update über hunderttausend Zeilen feuert hunderttausendmal. Halten Sie Before-Hooks schlank und verlagern Sie langsame Reaktionen in After-Hooks oder Hintergrund-Jobs.'
  - question: 'Kann ein Trigger eine Endlosschleife auslösen?'
    answer: 'Der Klassiker – ein Trigger, der in seine eigene Tabelle schreibt, löst sich selbst erneut aus, und ein afterSave, das das gerade verarbeitete Objekt speichert, ruft sich rekursiv auf, bis etwas bricht. Schützen Sie sich, indem Sie vor dem Schreiben prüfen, was sich tatsächlich geändert hat, und speichern Sie das auslösende Objekt nie ohne Abbruchbedingung aus seinem eigenen After-Hook heraus erneut.'
  - question: 'Können Trigger externe Dienste aufrufen?'
    answer: 'SQL-Trigger können praktisch nicht aus der Datenbank heraus – und sollten es auch nicht. Das ist der große Vorteil von Hooks auf Anwendungsebene: Ein afterSave in JavaScript kann eine Push-Benachrichtigung senden, jede beliebige API aufrufen oder einen ausgehenden Webhook auslösen, weil es im Backend-Prozess mit dem vollständigen Ökosystem läuft.'
  - question: 'Wofür wird beforeSave verwendet?'
    answer: 'Für die drei Aufgaben, die vor dem Schreibvorgang laufen müssen: Validierung (werfen Sie einen Fehler, und der Speichervorgang scheitert – für jeden Client), Normalisierung (Felder kürzen, abschneiden, kanonisieren) sowie Standard- und berechnete Werte. Nebenwirkungen gehören nicht dorthin – scheitert der Speichervorgang danach, ist die Wirkung längst eingetreten.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'PostgreSQL — CREATE TRIGGER'
    url: 'https://www.postgresql.org/docs/current/sql-createtrigger.html'
  - name: 'MySQL — Trigger Syntax and Examples'
    url: 'https://dev.mysql.com/doc/refman/8.4/en/trigger-syntax.html'
  - name: 'Cloud Code triggers guide'
    url: 'https://docs.parseplatform.org/cloudcode/guide/#beforesave-triggers'
  - name: 'Sequelize — Hooks lifecycle'
    url: 'https://sequelize.org/docs/v6/other-topics/hooks/'
  - name: 'Database trigger — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/Database_trigger'
cta:
  title: 'Regeln, die jeder Schreibvorgang befolgt'
  text: 'Registrieren Sie beforeSave und afterSave auf einer beliebigen Back4app-Klasse, und die Regel bindet jeden Client – SDKs, REST, GraphQL, Admin-Skripte – in JavaScript, das validieren, normalisieren, zählen, benachrichtigen und die Außenwelt aufrufen kann.'
  linkText: 'Kostenlos starten'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-09-25'
translationKey: database-triggers-beforesave-aftersave
---

**Ein Datenbank-Trigger ist ein Code-Hook, der bei Datenereignissen automatisch läuft – vor dem Speichern zum Validieren, danach zum Reagieren.** Die Idee hat zwei Abstammungslinien mit einem gemeinsamen Prinzip: den klassischen **SQL-Trigger**, prozeduralen Code, der in der Datenbank-Engine wohnt, und den modernen **Hook auf Anwendungsebene** – `beforeSave`, `afterSave`, `beforeDelete` – JavaScript, das pro Klasse im Backend registriert wird. Beide kodieren dasselbe Versprechen: Die Regel feuert bei *jedem* Schreibvorgang, gleich welcher Client, welches SDK oder welches Skript ihn ausgeführt hat – und genau das kann clientseitige Validierung nie versprechen.

## Das Wichtigste im Überblick

| Frage | Antwort |
| --- | --- |
| Das Prinzip | Code, an Datenereignisse gebunden – automatisch, pro Tabelle/Klasse, auf jedem Schreibpfad |
| Before = | Validieren · normalisieren · Standardwerte setzen – kann den Schreibvorgang noch ändern oder **abbrechen** |
| After = | Reagieren – zählen, benachrichtigen, synchronisieren; geschrieben ist geschrieben, also idempotent bleiben |
| Die zwei Formen | SQL-Trigger in der Engine · JS-Hooks im Backend-Prozess |
| Die klassischen Bugs | Versteckte Logik · endlose Selbstauslösung · langsame Trigger, die jeden Speichervorgang blockieren |

## Das Hook-Modell in der Praxis

Eine Validierungsregel und ihre Durchsetzung, von beiden Enden aus gesehen – der Server definiert sie einmal, und jeder Client überall erbt sie:

**JavaScript:**

```javascript
// JavaScript — Cloud Code (cloud/main.js)
// beforeSave: validate + normalize — can still change or ABORT the write
Parse.Cloud.beforeSave('Review', (req) => {
  const stars = req.object.get('stars');
  if (stars < 1 || stars > 5) throw 'Stars must be between 1 and 5';
  const comment = req.object.get('comment');
  if (comment && comment.length > 500) {
    req.object.set('comment', comment.slice(0, 497) + '…');
  }
});

// afterSave: side effects — the write already happened; be idempotent
Parse.Cloud.afterSave('Review', async (req) => {
  if (req.object.existed()) return; // count only NEW reviews, once
  await updateAverageStars(req.object.get('movie'));
});
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The hook fires for EVERY write path — this client included
final review = ParseObject('Review')
  ..set('movie', 'Arrival')
  ..set('stars', 9); // invalid — no client-side check needed
final response = await review.save();
print(response.error?.message); // "Stars must be between 1 and 5"
// The beforeSave hook rejected it server-side. A forged REST call,
// another SDK, an admin script — same rule, same rejection.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The hook fires for EVERY write path — this client included
var review = Review()
review.movie = "Arrival"
review.stars = 9 // invalid — no client-side check needed
do {
    _ = try await review.save()
} catch {
    print(error.localizedDescription) // "Stars must be between 1 and 5"
}
// The beforeSave hook rejected it server-side. A forged REST call,
// another SDK, an admin script — same rule, same rejection.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The hook fires for EVERY write path — this client included
val review = ParseObject("Review")
review.put("movie", "Arrival")
review.put("stars", 9) // invalid — no client-side check needed
try {
    review.save()
} catch (e: ParseException) {
    println(e.message) // "Stars must be between 1 and 5"
}
// The beforeSave hook rejected it server-side. A forged REST call,
// another SDK, an admin script — same rule, same rejection.
```

Die vollständige Hook-Familie überträgt dieselbe Form auf den gesamten Datenlebenszyklus: `beforeSave`/`afterSave`, `beforeDelete` (das Löschen eines Albums verhindern, das noch Fotos enthält) und `afterDelete` (seine Kinder aufräumen), dazu `beforeFind`/`afterFind`, um Abfragen umzuschreiben und Felder aus Ergebnissen zu entfernen – [CRUD](/glossary/de/crud-operationen/), eingepackt.

## Die klassische Form: SQL-Trigger

```sql
CREATE TRIGGER audit_price_change
AFTER UPDATE ON products
FOR EACH ROW                              -- Zeilenebene: feuert pro betroffener Zeile
WHEN (OLD.price IS DISTINCT FROM NEW.price)
EXECUTE FUNCTION log_price_change();      -- OLD und NEW halten beide Versionen
```

Die Taxonomie, die alle Datenbanken teilen, gemäß den Referenzen von [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtrigger.html) und [MySQL](https://dev.mysql.com/doc/refman/8.4/en/trigger-syntax.html): der **Zeitpunkt** – BEFORE (darf `NEW` ändern oder abbrechen), AFTER (reagiert auf die geschriebene Zeile), INSTEAD OF (ersetzt die Operation, vor allem bei Views); das **Ereignis** – Insert, Update, Delete; die **Granularität** – Zeilenebene gegenüber Anweisungsebene (ein Update über 10 Zeilen feuert einen Zeilen-Trigger 10-mal, einen Anweisungs-Trigger einmal). Zwei Semantiken zum Einprägen: SQL-Trigger laufen *in derselben Transaktion* wie der Schreibvorgang – scheitert der Trigger, wird die gesamte Operation zurückgerollt – und AFTER-Trigger feuern nur für Schreibvorgänge, die tatsächlich erfolgreich waren.

## Before vs. after: die Wahl richtet sich nach der Aufgabe

| | Before-Hooks | After-Hooks |
| --- | --- | --- |
| Kann die Daten ändern | **Ja** – mutieren, Standardwert setzen, abschneiden | Nein – es ist geschrieben |
| Kann den Schreibvorgang abbrechen | **Ja** – Fehler werfen, und der Speichervorgang scheitert | Nein |
| Richtig für | Validierung, Normalisierung, berechnete Felder | Zähler, Benachrichtigungen, Synchronisation, Audit |
| Falsch für | **Nebenwirkungen** – scheitert der Speichervorgang danach, ist die Wirkung schon eingetreten | Alles, was den Schreibvorgang hätte blockieren sollen |
| Verhalten im Fehlerfall | Weist die Operation ab, Fehler an den Client | Oft Fire-and-forget – Fehler landen in den Logs |
| Disziplin | Schnell – er blockiert jeden Speichervorgang | **Idempotent** – er läuft womöglich erneut |

Die Folgerungen, die kein Erklärtext ausspricht: Eine Nebenwirkung in einem Before-Hook ist ein Bug per Konstruktion (die E-Mail geht raus, dann scheitert der Speichervorgang), und ein nicht idempotenter After-Hook ist ein doppelter Zähler, der auf seinen Wiederholungsversuch wartet. In Hook-Systemen wie dem von Back4app läuft `afterSave` zu Ende, nachdem der Client seine Antwort bereits erhalten hat – Reaktionen sind von Natur aus asynchron, ihre Fehler müssen also verkraftbar und protokolliert sein.

```mermaid
flowchart LR
  accTitle: Lebenszyklus eines Schreibvorgangs durch Before- und After-Hooks
  accDescr: Ein Schreibvorgang von einem beliebigen Client durchläuft zuerst den Before-Hook, der ihn validieren, verändern oder abbrechen kann. Wird er zugelassen, schreibt die Datenbank ihn fest, und der After-Hook reagiert anschließend mit Nebenwirkungen wie Zählern, Benachrichtigungen und Webhooks, die idempotent sein müssen, weil sie mehr als einmal laufen können.
  C["Beliebiger Client<br/>SDK · REST · Skript"] --> B{"beforeSave<br/>validieren · normalisieren"}
  B -->|"throw"| X["Speichern abgelehnt –<br/>Fehler an den Client"]
  B -->|"zulassen (ggf. geändert)"| W[("Schreibvorgang wird festgeschrieben")]
  W --> A["afterSave<br/>Nebenwirkungen, idempotent"]
  A --> R["Zähler · Benachrichtigungen ·<br/>ausgehende Webhooks"]
```

## Hooks vs. SQL-Trigger

| | Hooks auf Anwendungsebene (beforeSave/afterSave) | SQL-Trigger |
| --- | --- | --- |
| Sprache | JavaScript plus das ganze SDK und sein Ökosystem | SQL / prozedurale SQL-Dialekte |
| Externe Aufrufe | Ja – APIs, Push, [Webhooks](/glossary/de/webhooks/) | Praktisch nein – und es ist nicht ihre Aufgabe |
| Lebt in | Ihrer Codebasis: versioniert, testbar, deployt | Dem Schema: in der Datenbank |
| Transaktion | Before-Hooks bewachen den Schreibvorgang; After-Hooks laufen nach der Antwort | Dieselbe Transaktion – ein Fehler rollt alles zurück |
| Bindet | Jede Anfrage über die Backend-API | Jeden Schreibvorgang auf der Tabelle, woher auch immer |
| Blinder Fleck | Direkte Schreibvorgänge in die Datenbank umgehen sie | Logik, die für Anwendungs-Debugger unsichtbar ist |

Die letzte Zeile ist die ehrliche Symmetrie: Die Garantie jeder Form endet an ihrer Schicht. Ein SQL-Trigger erwischt selbst eine wildgewordene psql-Sitzung, verbirgt die Logik aber vor dem Anwendungswerkzeug; ein Hook auf API-Ebene bindet jeden Client-Pfad durch das Backend, nicht aber den direkten Datenbankzugriff – weshalb Plattformen, denen das API-Gateway gehört (aller Verkehr fließt hindurch), in der Praxis das Beste aus beiden Welten bekommen. Die Fußnote zu ORMs gehört ebenfalls hierher: [ORM-Hooks](https://sequelize.org/docs/v6/other-topics/hooks/) feuern nur über den ORM – Massenoperationen und rohes SQL laufen einfach daran vorbei.

## Die Fallstricke, ehrlich benannt

**Versteckte Logik** ist der Klassiker: Ein Entwickler debuggt stundenlang seinen eigenen Code, während ein Trigger im Stillen Werte umschreibt – Trigger sind an der Aufrufstelle unsichtbar, dokumentieren Sie sie also und halten Sie ihre Zahl klein. **Endlosschleifen**: Ein Trigger, der in seine eigene Tabelle schreibt (oder ein `afterSave`, das sein eigenes Objekt speichert), löst sich selbst erneut aus; prüfen Sie, was sich geändert hat, und geben Sie der Rekursion eine Abbruchbedingung, bevor sie selbst eine findet. **Synchrone Kosten**: Before-Hooks sitzen in der Latenz jedes Schreibvorgangs – ein Hook von 200 ms macht jeden Speichervorgang 200 ms langsamer, und Massenschreibvorgänge multiplizieren die Auslösungen auf Zeilenebene mit der Zeilenzahl. **Kaskadenketten**: Trigger, die Trigger auslösen, die Trigger auslösen, machen aus einem Insert ein archäologisches Projekt. Die Dachregel aus Jahrzehnten der Praxis: Trigger setzen *Regeln* durch; sobald einer anfängt, einen *Ablauf* zu orchestrieren, verlagern Sie den Ablauf in [Funktionen](/glossary/de/cloud-code-serverless-funktionen/) oder [Jobs](/glossary/de/hintergrund-jobs/) und lassen den Trigger nur noch in die Queue einreihen.

## Typische Anwendungsfälle

- **Validierung, die jeder Client befolgt** – die Regel "Sterne zwischen 1 und 5", durchgesetzt gegen gefälschte Anfragen ebenso wie gegen künftige SDKs.
- **Audit-Spuren** – wer hat wann was geändert, geschrieben vom Ereignis selbst statt von kooperativen Clients.
- **Denormalisierte Zähler und berechnete Felder** – Durchschnitte, Anzahlen und suchfreundliche Duplikate, zum Schreibzeitpunkt aktuell gehalten.
- **Integrität in Kaskaden** – Löschvorgänge verweigern, solange Kinder existieren, oder die Kinder danach aufräumen.
- **Reaktive Nebenwirkungen** – ein `afterSave`, das eine [Push-Benachrichtigung](/glossary/de/push-benachrichtigungen/) oder einen [ausgehenden Webhook](/glossary/de/webhooks/) auslöst: aus dem Datenbankereignis wird ein Integrationsereignis.

## Welcher Hook wofür? Entscheidungsmatrix

| Die Aufgabe | Der Hook |
| --- | --- |
| Ungültige Daten abweisen | beforeSave – Fehler werfen |
| Kürzen, Standardwerte setzen, kanonisieren | beforeSave – Objekt mutieren |
| Einen Zähler oder ein Aggregat aktualisieren | afterSave – idempotent |
| Eine Person oder ein System benachrichtigen | afterSave → Push / Webhook / Queue |
| Gefährliche Löschvorgänge blockieren | beforeDelete |
| Nach Löschvorgängen aufräumen | afterDelete |
| Abfragen pro Nutzer eingrenzen | beforeFind |
| Lange oder langsame Reaktion | afterSave *reiht* einen [Job](/glossary/de/hintergrund-jobs/) *ein* – nie die Arbeit inline erledigen |

## Grenzen und Trade-offs

- **Unsichtbarkeit ist der Preis der Automatik.** Logik, die niemand aufruft, ist Logik, an die sich niemand erinnert; Benennung, Dokumentation und Code-Review halten die Magie prüfbar.
- **Hooks reichen nur so weit wie ihre Schicht.** API-Hooks übersehen direkte Schreibvorgänge in die Datenbank; SQL-Trigger übersehen nichts, verstecken sich aber vor Ihrem Werkzeug – wissen Sie, welchen blinden Fleck Sie gewählt haben.
- **Die Schreiblatenz ist das Budget.** Jeder Before-Hook zehrt davon; messen Sie Hooks so, wie Sie Abfragen messen.
- **After-Hooks sind letztendlich konsistent.** Zähler hinken Millisekunden hinterher und können doppelt feuern – gestalten Sie Lesevorgänge (und Wiederholungsversuche) entsprechend.
- **Trigger ersetzen keine Constraints.** Eindeutige Indizes, Fremdschlüssel und [ACLs](/glossary/de/zugriffskontrolllisten-acl/) greifen früher und billiger; Trigger übernehmen dort, wo deklarative Regeln aufhören.

## Trigger mit Back4app

Back4app ist eine Open-Source-Plattform für Backend as a Service (BaaS), die eine verwaltete Datenbank, automatisch generierte REST- und GraphQL-APIs, Authentifizierung, Dateispeicher und Serverless-Funktionen mit Cloud Code kombiniert. Trigger sind hier die Datenereignis-Familie von [Cloud Code](/glossary/de/cloud-code-serverless-funktionen/): Registrieren Sie `Parse.Cloud.beforeSave('Review', …)` einmal – die Code-Tabs zeigen das vollständige Muster – und die Regel bindet jeden Schreibvorgang, der über REST, GraphQL, ein beliebiges SDK oder das Dashboard hereinkommt, weil das API-Gateway der einzige Weg zu den Daten ist. Hooks erhalten reichhaltigen Kontext (`request.object`, `request.original`, `request.user`, Master-Key-Status), sodass die Validierung für Administratoren abweichen kann, `beforeFind` Abfragen zusätzlich zu den ACLs pro Nutzer eingrenzen kann und After-Hooks das gesamte JavaScript-Ökosystem für die Reaktionen zur Verfügung haben, die SQL-Trigger nicht erreichen – Push-Benachrichtigungen, ausgehende Webhooks, Einreihungen in Job-Queues. Mit Ihrem Code deployt, in git versioniert, testbar wie jede Funktion: die Garantie des Triggers, ohne seine Archäologie.
