---
term: 'Gatilhos de Banco de Dados (beforeSave e afterSave)'
seoTitle: 'Gatilhos de Banco de Dados: Hooks beforeSave e afterSave'
headline: 'O que são Gatilhos de Banco de Dados (beforeSave e afterSave)?'
slug: gatilhos-de-banco-de-dados
category: backend-compute
shortDefinition: 'Um gatilho de banco de dados é um hook de código que roda automaticamente em eventos de dados — antes do save para validar, depois dele para reagir.'
relatedTerms:
  - cloud-code-serverless-functions
  - webhooks
  - crud-operations
  - real-time-live-queries
contrastsWith:
  - webhooks
aboutTerms:
  - 'beforeSave'
  - 'afterSave'
  - 'Triggers BEFORE/AFTER'
faq:
  - question: 'O que é um trigger de banco de dados?'
    answer: 'É código procedural que executa automaticamente quando um evento de dados — insert, update, delete — ocorre em uma tabela ou classe específica. A forma clássica vive dentro do banco SQL; a forma moderna, em nível de aplicação, é uma função hook como beforeSave ou afterSave que o backend roda em volta de toda gravação.'
  - question: 'Qual a diferença entre triggers BEFORE e AFTER?'
    answer: 'Capacidade e timing. O BEFORE roda antes da gravação e pode validar, modificar os dados que chegam ou abortar a operação por completo. O AFTER roda depois que a gravação teve sucesso — não pode mudar o que aconteceu, só reagir: registrar, contar, notificar. O AFTER nunca dispara para gravações que falharam.'
  - question: 'Qual a diferença entre um trigger e uma stored procedure?'
    answer: 'A invocação. Uma stored procedure é chamada explicitamente, recebe parâmetros e retorna resultados. Um trigger nunca é chamado — ele dispara automaticamente quando seu evento ocorre, sem parâmetros, preso a uma tabela. A mesma maquinaria procedural, com o modelo de ativação oposto.'
  - question: 'A lógica deve ficar em triggers ou no código da aplicação?'
    answer: 'O consenso é um híbrido: triggers (ou hooks) para regras que precisam valer em todo caminho de gravação — validação, integridade, auditoria — e serviços da aplicação para workflows complexos. Hooks em nível de aplicação são o meio-termo moderno: as garantias do trigger, uma linguagem de verdade e controle de versão.'
  - question: 'Triggers prejudicam a performance?'
    answer: 'Eles rodam de forma síncrona dentro do caminho de gravação, então um trigger lento deixa todo save lento, e triggers em nível de linha se multiplicam em operações em massa — um update de cem mil linhas dispara cem mil vezes. Mantenha os before-hooks enxutos e empurre reações lentas para after-hooks ou jobs em segundo plano.'
  - question: 'Um trigger pode causar um loop infinito?'
    answer: 'Famosamente — um trigger que grava na própria tabela dispara a si mesmo de novo, e um afterSave que salva o objeto que acabou de tratar entra em recursão até algo quebrar. Proteja-se checando o que de fato mudou antes de gravar, e nunca salve de novo o objeto disparador dentro do próprio after-hook sem uma condição de parada.'
  - question: 'Triggers podem chamar serviços externos?'
    answer: 'Triggers SQL essencialmente não podem — nem deveriam — alcançar nada fora do banco. Essa é a vantagem de cartaz dos hooks em nível de aplicação: um afterSave em JavaScript pode enviar uma notificação push, chamar qualquer API ou disparar um webhook de saída, porque roda no processo do backend com o ecossistema inteiro disponível.'
  - question: 'Para que serve o beforeSave?'
    answer: 'Para os três trabalhos que exigem rodar antes da gravação: validação (lance um erro e o save falha, para qualquer cliente), normalização (trim, truncamento, canonicalização de campos) e defaults ou valores computados. Efeitos colaterais não pertencem ali — se o save falhar depois, o efeito já aconteceu.'
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: 'Regras que toda gravação obedece'
  text: 'Registre beforeSave e afterSave em qualquer classe do Back4app e a regra vale para todo cliente — SDKs, REST, GraphQL, scripts de admin — em JavaScript que pode validar, normalizar, contar, notificar e chamar o mundo lá fora.'
  linkText: 'Comece grátis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-08-21'
translationKey: database-triggers-beforesave-aftersave
---

**Um gatilho de banco de dados é um hook de código que roda automaticamente em eventos de dados — antes do save para validar, depois dele para reagir.** A ideia tem duas linhagens que compartilham um princípio: o **trigger SQL** clássico, código procedural vivendo dentro do motor do banco, e o **hook em nível de aplicação** moderno — `beforeSave`, `afterSave`, `beforeDelete` — JavaScript registrado por classe no backend. Ambos codificam a mesma promessa: a regra dispara para *toda* gravação, não importa qual cliente, SDK ou script a executou — que é exatamente o que a validação no cliente nunca pode prometer.

## Principais pontos

| Pergunta | Resposta |
| --- | --- |
| O princípio | Código preso a eventos de dados — automático, por tabela/classe, todo caminho de gravação |
| Before = | Validar · normalizar · aplicar defaults — ainda pode alterar ou **abortar** a gravação |
| After = | Reagir — contar, notificar, sincronizar; a gravação já aconteceu, então seja idempotente |
| As duas formas | Triggers SQL dentro do motor · hooks JS no processo do backend |
| Os bugs clássicos | Lógica escondida · loops infinitos de autodisparo · triggers lentos travando todo save |

## O modelo de hooks em ação

Uma regra de validação e sua aplicação, vistas das duas pontas — o servidor a define uma vez, e todo cliente em todo lugar a herda:

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

A família completa de hooks estende a mesma forma por todo o ciclo de vida dos dados: `beforeSave`/`afterSave`, `beforeDelete` (bloquear a exclusão de um Album que ainda tem Photos) e `afterDelete` (limpar os filhos dele), mais `beforeFind`/`afterFind` para reescrever queries e remover campos dos resultados — o [CRUD](/glossary/pt/operacoes-crud/), embrulhado.

## A forma clássica: triggers SQL

```sql
CREATE TRIGGER audit_price_change
AFTER UPDATE ON products
FOR EACH ROW                              -- nível de linha: dispara por linha afetada
WHEN (OLD.price IS DISTINCT FROM NEW.price)
EXECUTE FUNCTION log_price_change();      -- OLD e NEW guardam as duas versões
```

A taxonomia que todo banco compartilha, segundo as referências do [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtrigger.html) e do [MySQL](https://dev.mysql.com/doc/refman/8.4/en/trigger-syntax.html): **timing** — BEFORE (pode modificar `NEW` ou abortar), AFTER (reage à linha confirmada), INSTEAD OF (substitui a operação, principalmente em views); **evento** — insert, update, delete; **granularidade** — nível de linha versus nível de instrução (um update de 10 linhas dispara um trigger de linha 10 vezes, um de instrução uma vez). Duas semânticas que valem gravar: triggers SQL rodam *dentro da mesma transação* da gravação — uma falha no trigger desfaz a operação inteira — e triggers AFTER só disparam para gravações que de fato tiveram sucesso.

## Before vs. after: escolhidos pelo trabalho

| | Before hooks | After hooks |
| --- | --- | --- |
| Pode alterar os dados | **Sim** — mutar, aplicar default, truncar | Não — já foi gravado |
| Pode abortar a gravação | **Sim** — lance um erro, e o save falha | Não |
| Certo para | Validação, normalização, campos computados | Contadores, notificações, sincronização, auditoria |
| Errado para | **Efeitos colaterais** — se o save falhar depois, o efeito já disparou | Qualquer coisa que deveria ter bloqueado a gravação |
| Comportamento na falha | Rejeita a operação, erro para o cliente | Muitas vezes fire-and-forget — erros caem nos logs |
| Disciplina | Rápido — ele bloqueia todo save | **Idempotente** — pode rodar de novo |

Os corolários que nenhum explicador enuncia: um efeito colateral em um before-hook é um bug por construção (o e-mail sai, e então o save falha), e um after-hook que não é idempotente é um contador duplicado esperando um retry. Em sistemas de hooks como o do Back4app, o `afterSave` completa depois que o cliente já recebeu a resposta — reações são assíncronas por design, então suas falhas precisam ser toleráveis e logadas.

```mermaid
flowchart LR
  accTitle: Ciclo de vida da gravação através dos hooks before e after
  accDescr: Uma gravação vinda de qualquer cliente passa primeiro pelo hook before, que pode validá-la, modificá-la ou abortá-la. Se permitida, o banco de dados confirma a gravação, e o hook after então reage com efeitos colaterais como contadores, notificações e webhooks, que precisam ser idempotentes porque podem rodar mais de uma vez.
  C["Qualquer cliente<br/>SDK · REST · script"] --> B{"beforeSave<br/>valida · normaliza"}
  B -->|"throw"| X["Save rejeitado —<br/>erro para o cliente"]
  B -->|"permite (talvez modificado)"| W[("Gravação confirmada")]
  W --> A["afterSave<br/>efeitos colaterais, idempotentes"]
  A --> R["Contadores · notificações ·<br/>webhooks de saída"]
```

## Hooks vs. triggers SQL

| | Hooks de aplicação (beforeSave/afterSave) | Triggers SQL |
| --- | --- | --- |
| Linguagem | JavaScript + o SDK e o ecossistema inteiros | SQL / dialetos procedurais de SQL |
| Chamadas externas | Sim — APIs, push, [webhooks](/glossary/pt/webhooks/) | Essencialmente não — e nem deveriam |
| Vive em | Seu codebase: versionado, testável, deployado | O schema: dentro do banco de dados |
| Transação | Before-hooks são o portão da gravação; after-hooks rodam pós-resposta | Mesma transação — a falha desfaz tudo |
| Vale para | Toda requisição que passa pela API do backend | Toda gravação na tabela, venha de onde vier |
| Ponto cego | Gravações diretas no banco passam por fora | Lógica invisível aos debuggers da aplicação |

A última linha é a simetria honesta: a garantia de cada forma está limitada à sua camada. Um trigger SQL pega até uma sessão psql renegada, mas esconde lógica do ferramental da aplicação; um hook em nível de API cobre todo caminho de cliente através do backend, mas não o acesso cru ao banco — e é por isso que plataformas donas do gateway de API (todo o tráfego flui por ele) ficam, na prática, com o melhor dos dois. A nota de rodapé sobre ORMs entra aqui também: [hooks de ORM](https://sequelize.org/docs/v6/other-topics/hooks/) só disparam através do ORM — operações em massa e SQL cru passam reto por eles.

## As armadilhas, com honestidade

**Lógica escondida** é o clássico: um desenvolvedor depura o próprio código por horas enquanto um trigger reescreve valores em silêncio — triggers são invisíveis no ponto de chamada, então documente-os e mantenha-os poucos. **Loops infinitos**: um trigger gravando na própria tabela dispara a si mesmo de novo (e um `afterSave` salvando o próprio objeto também); proteja-se checando o que mudou, e dê à recursão uma condição de parada antes que ela encontre uma por você. **Custo síncrono**: before-hooks moram dentro da latência de toda gravação — um hook de 200 ms deixa todo save 200 ms mais lento, e gravações em massa multiplicam os disparos por linha pela contagem de linhas. **Cadeias em cascata**: triggers que disparam triggers que disparam triggers transformam um insert em um projeto de arqueologia. A regra guarda-chuva de décadas de prática: triggers aplicam *regras*; no momento em que um começa a orquestrar um *workflow*, mova o workflow para [funções](/glossary/pt/cloud-code-funcoes-serverless/) ou [jobs](/glossary/pt/jobs-em-segundo-plano/) e deixe o trigger apenas enfileirar.

## Casos de uso comuns

- **Validação que todo cliente obedece** — a regra de estrelas-entre-1-e-5, aplicada contra requisições forjadas e SDKs futuros igualmente.
- **Trilhas de auditoria** — quem mudou o quê, quando, escritas pelo próprio evento em vez de por clientes cooperativos.
- **Contadores desnormalizados e campos computados** — médias, contagens e duplicatas amigáveis à busca mantidas verdadeiras no momento da gravação.
- **Integridade em cascata** — recusar exclusões com filhos, ou limpar os filhos depois.
- **Efeitos colaterais reativos** — um `afterSave` disparando uma [notificação push](/glossary/pt/notificacoes-push/) ou um [webhook de saída](/glossary/pt/webhooks/): o evento de banco de dados virando evento de integração.

## Qual hook onde? Matriz de decisão

| O trabalho | O hook |
| --- | --- |
| Rejeitar dados ruins | beforeSave — lance um erro |
| Trim, default, canonicalizar | beforeSave — mute o objeto |
| Atualizar um contador ou agregado | afterSave — com idempotência |
| Notificar uma pessoa ou sistema | afterSave → push / webhook / fila |
| Bloquear exclusões perigosas | beforeDelete |
| Limpar depois de exclusões | afterDelete |
| Aplicar escopo de query por usuário | beforeFind |
| Reação longa ou lenta | afterSave *enfileira* um [job](/glossary/pt/jobs-em-segundo-plano/) — nunca faz o trabalho inline |

## Limitações e trade-offs

- **Invisibilidade é o preço da automaticidade.** Lógica que ninguém chama é lógica que ninguém lembra; nomes, docs e code review mantêm a mágica auditável.
- **Hooks têm o escopo da sua camada.** Hooks de API não veem gravações diretas no banco; triggers SQL não perdem nada, mas se escondem do seu ferramental — saiba qual ponto cego você escolheu.
- **A latência de gravação é o orçamento.** Todo before-hook gasta dele; meça hooks como você mede queries.
- **After-hooks são eventualmente consistentes.** Contadores atrasam milissegundos e podem disparar em dobro — projete as leituras (e os retries) de acordo.
- **Triggers não substituem constraints.** Índices únicos, chaves estrangeiras e [ACLs](/glossary/pt/listas-de-controle-de-acesso-acl/) aplicam mais barato e mais cedo; triggers começam onde as regras declarativas param.

## Gatilhos de Banco de Dados no Back4app

O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. Gatilhos aqui são a família de eventos de dados do [Cloud Code](/glossary/pt/cloud-code-funcoes-serverless/): registre `Parse.Cloud.beforeSave('Review', …)` uma vez — as abas de código mostram o padrão inteiro — e a regra vale para toda gravação que chega por REST, GraphQL, qualquer SDK ou o dashboard, porque o gateway de API é o único caminho até os dados. Os hooks recebem contexto rico (`request.object`, `request.original`, `request.user`, status de master key), então a validação pode diferir para admins, o `beforeFind` pode aplicar escopo por usuário em cima das ACLs, e os after-hooks têm o ecossistema JavaScript inteiro para as reações que triggers SQL não alcançam — notificações push, webhooks de saída, enfileiramento de jobs. Deployado com o seu código, versionado no git, testável como qualquer função: a garantia do trigger, sem a arqueologia do trigger.
