FFastero

Connect any database. Ask in plain English.

Try free
Back to blog

Blog article

How to Build Data Workflows That Trigger on Database Changes

Schedule-based workflows miss the moment. Database-triggered workflows fire when data actually changes — a new payment, a schema alteration, a CDC event. Here's how to wire them up with SQL and Python steps, no orchestration framework required.

Fastero Dev TeamFastero Dev Team
2026-08-08
workflowstriggersdatabasesautomationdata-pipelines
How to Build Data Workflows That Trigger on Database Changes

Database-triggered workflows run when a row changes, a table updates, or a schema drifts. Not when a cron timer says so. You define the database event you care about, attach SQL and Python steps, and the workflow fires within seconds of the change. Fastero's workflow builder handles the wiring: trigger detection, step sequencing, conditional branching.

How does a database-triggered workflow actually work?

The shape is always the same:

┌─────────────┐     ┌──────────┐     ┌──────────────┐     ┌──────────┐
│  Database   │     │ Trigger  │     │    Steps     │     │  Action  │
│  change     │ ──→ │ (detect  │ ──→ │  (SQL /      │ ──→ │ (alert,  │
│  (row/DDL)  │     │  event)  │     │   Python)    │     │  update) │
└─────────────┘     └──────────┘     └──────────────┘     └──────────┘

A trigger watches your database for a specific type of change. Postgres LISTEN/NOTIFY, Snowflake streams, MySQL binlog CDC, BigQuery table update notifications. When the event fires, context from the triggering change flows into the first step as input variables. Each step's output feeds the next. Conditions between steps let you branch or stop early.

This is push-based, not poll-based. Airflow sensors hold a worker slot while checking "has the thing happened yet?" every N seconds. Database triggers sit at the source. Nothing runs until something changes.

What does a Stripe payment trigger look like in practice?

A common pattern: Stripe webhook fires on payment_intent.succeeded, the workflow enriches the payment with customer data from Postgres, updates the CRM deal, and posts to Slack.

 Stripe webhook ──→ Query customer ──→ Update HubSpot ──→ Slack alert
 (payment.succeeded)  from Postgres      deal amount       #sales channel

The first step queries your database for context the webhook payload doesn't carry:

SELECT
  c.company_name,
  c.hubspot_deal_id,
  c.account_owner,
  p.amount / 100.0 AS payment_amount,
  p.currency
FROM customers c
JOIN stripe_payments p ON p.customer_id = c.stripe_id
WHERE p.stripe_payment_id = '{{ trigger.payment_intent_id }}'

{{ trigger.payment_intent_id }} comes from the webhook payload. No explicit serialization between steps. company_name, hubspot_deal_id, payment_amount are available to every downstream step automatically. No XCom. No artifact store.

A Python step updates HubSpot:

import requests
 
requests.patch(
    f'https://api.hubapi.com/crm/v3/objects/deals/{hubspot_deal_id}',
    headers={'Authorization': f'Bearer {hubspot_api_key}'},
    json={
        'properties': {
            'amount': payment_amount,
            'dealstage': 'closedwon',
            'closedate': trigger['created_at']
        }
    }
)
 
output = {
    'company': company_name,
    'amount': payment_amount,
    'owner': account_owner
}

The Slack step fires last: "Payment received: ${{ amount }} from {{ company }}. Deal updated in HubSpot. Owner: {{ owner }}." The whole chain runs in under 10 seconds from the Stripe event.

Try building this in Zapier. You'll get the webhook trigger and the Slack message. But the SQL query against your Postgres database? Zapier doesn't speak SQL. You'd need a separate API layer in front of your database just to give Zapier something to call.

Fastero

Connect your database. Ask questions. Get dashboards.

Postgres, BigQuery, Snowflake, and 10+ sources — live-connected, AI-powered, no dashboard builder learning curve.

Try free →

How do you catch schema changes before they break things?

Schema drift kills downstream pipelines silently. A column gets renamed, a type changes from integer to varchar, a new NOT NULL constraint appears. Your transforms fail at 3am and nobody knows why until the morning standup.

Database-level event triggers can fire on DDL changes. When a schema change lands, the workflow inspects the alteration and runs quality checks against the affected table:

-- Step 1: What changed?
SELECT
  table_schema,
  table_name,
  column_name,
  data_type,
  is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
  AND table_name = '{{ trigger.table_name }}'
ORDER BY ordinal_position
 
-- Step 2: Quality check the altered table
SELECT
  COUNT(*)                            AS row_count,
  COUNT(*) FILTER (WHERE id IS NULL)  AS null_ids,
  MAX(updated_at)                     AS last_update
FROM {{ trigger.table_name }}

If null IDs appear or the row count dropped unexpectedly, the email step fires with a report. Otherwise, the workflow logs the change and stops. No 3am pages for a harmless column addition.

The Airflow version of this requires a custom sensor polling information_schema on an interval, a PythonOperator for the quality checks, and an EmailOperator wired with Jinja templates. Three operators, a DAG definition, and a deployment for what's really "when a table changes shape, check it."

What about the daily aggregate-and-refresh case?

Not every workflow needs a database event trigger. Daily metric aggregation is the textbook cron case, and it works with the same builder.

trigger:
  type: cron
  schedule: "30 6 * * *"
 
steps:
  - name: aggregate_daily_metrics
    action: run_sql
    sql: |
      INSERT INTO analytics.daily_kpis
      SELECT
        current_date - 1          AS metric_date,
        COUNT(DISTINCT user_id)   AS dau,
        SUM(revenue)              AS total_revenue,
        COUNT(*) FILTER (WHERE is_new_user) AS new_signups
      FROM events
      WHERE event_date = current_date - 1;
    store_result: aggregation
 
  - name: refresh_dashboard
    action: refresh_dashboard
    dashboard: executive-kpis
 
  - name: notify_team
    action: send_notification
    condition: "{{ aggregation.row_count > 0 }}"
    channel: slack
    destination: "#business-metrics"
    message: "Daily KPIs updated. {{ aggregation.total_revenue }} revenue, {{ aggregation.dau }} DAU."

Three steps, one trigger. The condition on the Slack step prevents a notification when the aggregation returned nothing (which usually means the source data hasn't landed yet). If you want to catch missing data, set up a separate alert workflow for that.

How does this compare to Airflow, Dagster, Zapier, and n8n?

Two camps, and neither covers the middle:

Airflow / Dagster Zapier / n8n Fastero
SQL steps Yes (via operators) No Yes (native)
Database triggers Sensors (poll-based) "New row" only Push-based CDC
Conditional branching BranchPythonOperator Basic filters Visual conditions
Infrastructure needed Scheduler + workers None (SaaS) None (SaaS)
Setup for 3 steps Hours to days Minutes Minutes
Custom Python Yes Limited Yes

Airflow and Dagster give you full programmatic control. That matters when you have 200 DAGs with complex dependency trees and fan-out/fan-in patterns. But for the 3-5 step workflows that make up the majority of real-world automation, you're paying an infrastructure and code tax for capability you don't use.

Zapier and n8n are fast to set up but can't query your database. Their triggers are SaaS-event-based ("new row in Google Sheet"), not database-change-based. The moment your workflow needs a SQL join or a Python transformation, you've outgrown them.

For deeper coverage of event-driven pipelines without Airflow or building multi-step workflows visually, those posts go into each angle separately.

FAQ

Can I trigger a workflow from any database? Fastero supports Postgres (LISTEN/NOTIFY, logical replication), Snowflake (streams, stage monitoring), MySQL (binlog CDC), and BigQuery (table update notifications). For databases without native change events, a webhook trigger works as a universal adapter.

How fast do database triggers fire after the change happens? Postgres LISTEN/NOTIFY fires within seconds. Snowflake stream checks run on a configurable interval, default 60 seconds. Webhook triggers from services like Stripe typically fire in under 5 seconds.

What happens if a workflow fails mid-step? Each step has independent error handling: retry with backoff, skip and continue, or abort and notify. A failure at step 2 configured to abort stops the workflow, sends a failure alert to a channel you choose, and preserves step-1 results in the run log.

Can I combine database triggers with cron schedules? Yes. A common pattern: a database trigger handles real-time reactions (each payment as it arrives), while a daily cron workflow reconciles totals and generates a summary report. Both can target the same data source.

Do I need to install anything in my database? For Postgres LISTEN/NOTIFY, you add a trigger function on the table. It's a few lines of PL/pgSQL that Fastero provides. Snowflake streams require creating the stream object in Snowflake. Webhook-based triggers need nothing on the database side.


Try Fastero free — build database-triggered workflows with SQL, Python, and visual orchestration. No credit card required.

Ready to try it yourself?

Connect your database, ask questions in plain English, and get live dashboards — in under 2 minutes. No credit card required.