---
term: 'Triggers de Base de Données (beforeSave et afterSave)'
seoTitle: 'Triggers de base de données : les hooks beforeSave et afterSave'
headline: 'Que sont les triggers de base de données (beforeSave et afterSave) ?'
slug: triggers-de-base-de-donnees
category: backend-compute
shortDefinition: 'Un trigger de base de données est un hook de code qui s''exécute automatiquement sur les événements de données : avant un save pour valider, après pour réagir.'
relatedTerms:
  - cloud-code-serverless-functions
  - webhooks
  - crud-operations
  - real-time-live-queries
contrastsWith:
  - webhooks
aboutTerms:
  - 'beforeSave'
  - 'afterSave'
  - 'BEFORE/AFTER Triggers'
faq:
  - question: 'Qu''est-ce qu''un trigger de base de données ?'
    answer: 'Du code procédural qui s''exécute automatiquement lorsqu''un événement de données — insert, update, delete — survient sur une table ou une classe précise. La forme classique vit à l''intérieur de la base de données SQL ; la forme moderne, applicative, est une fonction hook comme beforeSave ou afterSave que le backend exécute autour de chaque écriture.'
  - question: 'Quelle est la différence entre les triggers BEFORE et AFTER ?'
    answer: 'La capacité et le moment. BEFORE s''exécute avant l''écriture et peut valider, modifier les données entrantes ou annuler entièrement l''opération. AFTER s''exécute une fois l''écriture réussie — il ne peut plus changer ce qui s''est passé, seulement réagir : journaliser, compter, notifier. AFTER ne se déclenche jamais pour une écriture qui a échoué.'
  - question: 'Quelle est la différence entre un trigger et une procédure stockée ?'
    answer: 'L''invocation. Une procédure stockée est appelée explicitement, reçoit des paramètres et renvoie des résultats. Un trigger n''est jamais appelé — il se déclenche automatiquement quand son événement survient, sans paramètres, attaché à une table. La même mécanique procédurale, avec le modèle d''activation inverse.'
  - question: 'La logique doit-elle vivre dans les triggers ou dans le code applicatif ?'
    answer: 'Le consensus est hybride : les triggers (ou hooks) pour les règles qui doivent tenir sur tous les chemins d''écriture — validation, intégrité, audit — et les services applicatifs pour les workflows complexes. Les hooks applicatifs sont le juste milieu moderne : les garanties du trigger, un vrai langage de programmation, le contrôle de version.'
  - question: 'Les triggers dégradent-ils les performances ?'
    answer: 'Ils s''exécutent de façon synchrone dans le chemin d''écriture : un trigger lent ralentit chaque save, et les triggers au niveau ligne se multiplient sur les opérations de masse — un update de cent mille lignes déclenche cent mille exécutions. Gardez les before-hooks légers et repoussez les réactions lentes vers les after-hooks ou des jobs en arrière-plan.'
  - question: 'Un trigger peut-il provoquer une boucle infinie ?'
    answer: 'Le grand classique — un trigger qui écrit dans sa propre table se redéclenche lui-même, et un afterSave qui sauvegarde l''objet qu''il vient de traiter s''appelle récursivement jusqu''à ce que quelque chose casse. Protégez-vous en vérifiant ce qui a réellement changé avant d''écrire, et ne re-sauvegardez jamais l''objet déclencheur depuis son propre after-hook sans condition d''arrêt.'
  - question: 'Les triggers peuvent-ils appeler des services externes ?'
    answer: 'Les triggers SQL ne peuvent pour ainsi dire pas — et ne devraient pas — sortir de la base de données. C''est l''avantage majeur des hooks applicatifs : un afterSave en JavaScript peut envoyer une notification push, appeler n''importe quelle API ou déclencher un webhook sortant, parce qu''il tourne dans le processus backend avec tout l''écosystème à disposition.'
  - question: 'À quoi sert beforeSave ?'
    answer: 'Aux trois tâches qui exigent de s''exécuter avant l''écriture : la validation (levez une erreur et le save échoue, pour tous les clients), la normalisation (trim, troncature, canonicalisation des champs) et les valeurs par défaut ou calculées. Les effets de bord n''y ont pas leur place — si le save échoue ensuite, l''effet a déjà eu lieu.'
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: 'Des règles que toute écriture respecte'
  text: 'Enregistrez beforeSave et afterSave sur n''importe quelle classe Back4app et la règle s''impose à tous les clients — SDK, REST, GraphQL, scripts d''administration — en JavaScript, capable de valider, normaliser, compter, notifier et appeler le monde extérieur.'
  linkText: 'Commencez gratuitement'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-09-14'
translationKey: database-triggers-beforesave-aftersave
---

**Un trigger de base de données est un hook de code qui s'exécute automatiquement sur les événements de données : avant un save pour valider, après pour réagir.** L'idée a deux lignées qui partagent un même principe : le **trigger SQL** classique, du code procédural logé dans le moteur de base de données, et le **hook applicatif** moderne — `beforeSave`, `afterSave`, `beforeDelete` — du JavaScript enregistré par classe dans le backend. Les deux encodent la même promesse : la règle se déclenche pour *chaque* écriture, quel que soit le client, le SDK ou le script qui l'a effectuée — ce que la validation côté client ne pourra jamais promettre.

## Points clés

| Question | Réponse |
| --- | --- |
| Le principe | Du code lié aux événements de données — automatique, par table/classe, sur tous les chemins d'écriture |
| Before = | Valider · normaliser · appliquer des valeurs par défaut — peut encore modifier ou **annuler** l'écriture |
| After = | Réagir — compter, notifier, synchroniser ; l'écriture a déjà eu lieu, soyez donc idempotent |
| Les deux formes | Triggers SQL dans le moteur · hooks JS dans le processus backend |
| Les bugs classiques | Logique cachée · boucles infinies d'auto-déclenchement · triggers lents qui bloquent chaque save |

## Le modèle de hooks en action

Une règle de validation et son application, vues des deux côtés — le serveur la définit une fois, et tous les clients, partout, en héritent :

**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.
```

La famille complète des hooks étend la même forme à tout le cycle de vie des données : `beforeSave`/`afterSave`, `beforeDelete` (empêcher la suppression d'un Album qui a encore des Photos) et `afterDelete` (nettoyer ses enfants), plus `beforeFind`/`afterFind` pour réécrire les requêtes et retirer des champs des résultats — le [CRUD](/glossary/fr/operations-crud/), enveloppé.

## La forme classique : les triggers SQL

```sql
CREATE TRIGGER audit_price_change
AFTER UPDATE ON products
FOR EACH ROW                              -- niveau ligne : se déclenche par ligne affectée
WHEN (OLD.price IS DISTINCT FROM NEW.price)
EXECUTE FUNCTION log_price_change();      -- OLD et NEW contiennent les deux versions
```

La taxonomie que toutes les bases de données partagent, selon les références [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtrigger.html) et [MySQL](https://dev.mysql.com/doc/refman/8.4/en/trigger-syntax.html) : le **moment** — BEFORE (peut modifier `NEW` ou annuler), AFTER (réagit à la ligne validée), INSTEAD OF (remplace l'opération, surtout pour les vues) ; l'**événement** — insert, update, delete ; la **granularité** — niveau ligne contre niveau instruction (un update de 10 lignes déclenche 10 fois un trigger de ligne, une seule fois un trigger d'instruction). Deux sémantiques à graver : les triggers SQL s'exécutent *dans la même transaction* que l'écriture — un échec du trigger annule toute l'opération — et les triggers AFTER ne se déclenchent que pour les écritures réellement réussies.

## Before vs. after : le choix se fait par tâche

| | Before hooks | After hooks |
| --- | --- | --- |
| Peut modifier les données | **Oui** — muter, appliquer une valeur par défaut, tronquer | Non — c'est écrit |
| Peut annuler l'écriture | **Oui** — levez une erreur et le save échoue | Non |
| Adapté à | Validation, normalisation, champs calculés | Compteurs, notifications, synchronisation, audit |
| Inadapté à | **Les effets de bord** — si le save échoue ensuite, l'effet a déjà eu lieu | Tout ce qui aurait dû bloquer l'écriture |
| Comportement en cas d'échec | Rejette l'opération, erreur renvoyée au client | Souvent fire-and-forget — les erreurs finissent dans les logs |
| Discipline | Rapide — il bloque chaque save | **Idempotent** — il peut s'exécuter à nouveau |

Les corollaires qu'aucune explication n'énonce : un effet de bord dans un before-hook est un bug par construction (l'e-mail part, puis le save échoue), et un after-hook non idempotent est un compteur en double qui attend sa nouvelle tentative. Dans les systèmes de hooks comme celui de Back4app, `afterSave` se termine après que le client a déjà reçu sa réponse — les réactions sont asynchrones par conception, donc leurs échecs doivent être tolérables et journalisés.

```mermaid
flowchart LR
  accTitle: Cycle de vie d'une écriture à travers les hooks before et after
  accDescr: Une écriture provenant de n'importe quel client passe d'abord par le hook before, qui peut la valider, la modifier ou l'annuler. Si elle est autorisée, la base de données valide l'écriture, puis le hook after réagit avec des effets de bord comme des compteurs, des notifications et des webhooks, qui doivent être idempotents car ils peuvent s'exécuter plusieurs fois.
  C["N'importe quel client<br/>SDK · REST · script"] --> B{"beforeSave<br/>valide · normalise"}
  B -->|"throw"| X["Save rejeté —<br/>erreur au client"]
  B -->|"autorise (éventuellement modifié)"| W[("Écriture validée")]
  W --> A["afterSave<br/>effets de bord, idempotents"]
  A --> R["Compteurs · notifications ·<br/>webhooks sortants"]
```

## Hooks vs. triggers SQL

| | Hooks applicatifs (beforeSave/afterSave) | Triggers SQL |
| --- | --- | --- |
| Langage | JavaScript + tout le SDK et son écosystème | SQL / dialectes SQL procéduraux |
| Appels externes | Oui — API, push, [webhooks](/glossary/fr/webhooks/) | Pour ainsi dire non — et ce n'est pas leur rôle |
| Vit dans | Votre base de code : versionnée, testable, déployée | Le schéma : à l'intérieur de la base de données |
| Transaction | Les before-hooks gardent l'écriture ; les after-hooks tournent après la réponse | Même transaction — l'échec annule tout |
| Couvre | Toute requête passant par l'API du backend | Toute écriture dans la table, d'où qu'elle vienne |
| Angle mort | Les écritures directes en base le contournent | Logique invisible aux debuggers applicatifs |

La dernière ligne est la symétrie honnête : la garantie de chaque forme se limite à sa couche. Un trigger SQL attrape même une session psql sauvage mais cache la logique à l'outillage applicatif ; un hook au niveau de l'API couvre tous les chemins clients passant par le backend mais pas l'accès brut à la base — d'où le fait que les plateformes qui possèdent la passerelle d'API (tout le trafic y transite) obtiennent en pratique le meilleur des deux. La note de bas de page sur les ORM a aussi sa place ici : les [hooks d'ORM](https://sequelize.org/docs/v6/other-topics/hooks/) ne se déclenchent qu'à travers l'ORM — les opérations de masse et le SQL brut passent à côté.

## Les pièges, honnêtement

**La logique cachée** est le grand classique : un développeur débogue son propre code pendant des heures pendant qu'un trigger réécrit des valeurs en silence — les triggers sont invisibles au point d'appel, donc documentez-les et gardez-en peu. **Les boucles infinies** : un trigger qui écrit dans sa propre table (ou un `afterSave` qui sauvegarde son propre objet) se redéclenche lui-même ; vérifiez ce qui a changé, et donnez à la récursion une condition d'arrêt avant qu'elle n'en trouve une pour vous. **Le coût synchrone** : les before-hooks sont dans la latence de chaque écriture — un hook de 200 ms rend chaque save 200 ms plus lent, et les écritures de masse multiplient les déclenchements par ligne par le nombre de lignes. **Les cascades** : des triggers qui déclenchent des triggers qui déclenchent des triggers transforment un insert en chantier d'archéologie. La règle générale héritée de décennies de pratique : les triggers appliquent des *règles* ; dès que l'un d'eux commence à orchestrer un *workflow*, déplacez le workflow vers des [fonctions](/glossary/fr/cloud-code-fonctions-serverless/) ou des [jobs](/glossary/fr/jobs-en-arriere-plan/) et laissez le trigger se contenter de mettre en file d'attente.

## Cas d'usage courants

- **La validation que tout client respecte** — la règle des étoiles entre 1 et 5, appliquée aussi bien aux requêtes forgées qu'aux futurs SDK.
- **Les pistes d'audit** — qui a changé quoi, et quand, écrit par l'événement lui-même plutôt que par des clients coopératifs.
- **Les compteurs dénormalisés et les champs calculés** — moyennes, décomptes et duplications utiles à la recherche, tenus à jour au moment de l'écriture.
- **L'intégrité en cascade** — refuser les suppressions tant qu'il reste des enfants, ou nettoyer les enfants après coup.
- **Les effets de bord réactifs** — un `afterSave` qui déclenche une [notification push](/glossary/fr/notifications-push/) ou un [webhook sortant](/glossary/fr/webhooks/) : l'événement de base de données devenu événement d'intégration.

## Quel hook et où ? Matrice de décision

| La tâche | Le hook |
| --- | --- |
| Rejeter des données invalides | beforeSave — levez une erreur |
| Nettoyer, appliquer une valeur par défaut, canonicaliser | beforeSave — mutez l'objet |
| Mettre à jour un compteur ou un agrégat | afterSave — de façon idempotente |
| Notifier une personne ou un système | afterSave → push / webhook / file d'attente |
| Bloquer les suppressions dangereuses | beforeDelete |
| Nettoyer après une suppression | afterDelete |
| Limiter la portée des requêtes par utilisateur | beforeFind |
| Réaction longue ou lente | afterSave *met en file* un [job](/glossary/fr/jobs-en-arriere-plan/) — ne fait jamais le travail en ligne |

## Limites et trade-offs

- **L'invisibilité est le prix de l'automatisme.** Une logique que personne n'appelle est une logique dont personne ne se souvient ; nommage, documentation et revue de code gardent la magie auditable.
- **Les hooks ont la portée de leur couche.** Les hooks d'API ne voient pas les écritures directes en base ; les triggers SQL ne ratent rien mais se cachent de votre outillage — sachez quel angle mort vous avez choisi.
- **La latence d'écriture est le budget.** Chaque before-hook y puise ; mesurez les hooks comme vous mesurez les requêtes.
- **Les after-hooks sont cohérents à terme.** Les compteurs prennent quelques millisecondes de retard et peuvent se déclencher deux fois — concevez les lectures (et les nouvelles tentatives) en conséquence.
- **Les triggers ne remplacent pas les contraintes.** Index uniques, clés étrangères et [ACL](/glossary/fr/listes-de-controle-d-acces-acl/) s'appliquent plus tôt et pour moins cher ; les triggers prennent le relais là où les règles déclaratives s'arrêtent.

## Les triggers sur Back4app

Back4app est une plateforme open-source de Backend as a Service (BaaS) qui combine une base de données gérée, des API REST et GraphQL générées automatiquement, l'authentification, le stockage de fichiers et des fonctions serverless avec Cloud Code. Les triggers y sont la famille « événements de données » de [Cloud Code](/glossary/fr/cloud-code-fonctions-serverless/) : enregistrez `Parse.Cloud.beforeSave('Review', …)` une fois — les onglets de code montrent le motif complet — et la règle s'impose à toute écriture arrivant par REST, GraphQL, n'importe quel SDK ou le dashboard, parce que la passerelle d'API est le seul chemin vers les données. Les hooks reçoivent un contexte riche (`request.object`, `request.original`, `request.user`, statut master key), donc la validation peut différer pour les administrateurs, `beforeFind` peut restreindre les requêtes par utilisateur par-dessus les ACL, et les after-hooks disposent de tout l'écosystème JavaScript pour les réactions que les triggers SQL n'atteignent pas — notifications push, webhooks sortants, mises en file de jobs. Déployé avec votre code, versionné dans git, testable comme n'importe quelle fonction : la garantie du trigger, sans son archéologie.
