Airtable to Supabase sync (new rows, updates, backfill) — a practical migration plan
🧠

Airtable to Supabase sync (new rows, updates, backfill) — a practical migration plan

View Details
Unsplash Cover
Unsplash Cover
Video
General
🔑 Keyword Goals
Generate LK
Generate NL
Connie's Keyword Target
airtable to supabase sync
Hidden
Full URL
Unsplash Cover
Unsplash Cover
Last edited time
Jul 27, 2026 05:54 PM GMT+0

Why this migration breaks (and how to avoid it)

An Airtable to Supabase sync sounds straightforward — until you run it twice and end up with duplicate rows. Airtable “just works” because it mixes storage, UI, and computed logic. Supabase is Postgres + APIs — you get more control, but you have to make data integrity explicit.
Photo by Woliul Hasan on Unsplash
Photo by Woliul Hasan on Unsplash
Common failure modes:
  • Row duplication when a sync runs twice or retries after a timeout
  • Broken relations (linked records) when you don’t have stable IDs
  • “Computed” Airtable fields (rollups/lookups/formulas) getting treated like source-of-truth columns
  • Trigger storms (Zapier/automations) causing race conditions

Phase 0 — Map your raw Airtable tables (no computed fields yet)

Goal: mirror Airtable’s base into Supabase with the minimum structure needed for reliable syncing.
  1. Export your table/field inventory
    • Field types (text, select, multi-select, attachment, date, linked record, etc.)
    • Which fields are computed (formulas, rollups, lookups) → exclude from Phase 0
  2. Decide your “Airtable record id” strategy
    • Add a column on every Supabase table: airtable_record_id (TEXT, NOT NULL)
    • Add a unique constraint: UNIQUE(airtable_record_id)
  3. Decide how you’ll represent linked records
    • Quick + safe early approach: store linked Airtable record IDs in join tables (or as arrays/JSON if you must), then normalize later
    • Don’t try to perfectly model every relationship before the pipeline works — you can refactor once the data is stable

Phase 1 — Create Supabase tables + constraints (the anti-duplication foundation)

For each Airtable table:
  • Create a Supabase table with:
    • airtable_record_id (unique)
    • raw fields you actually need operationally
    • created_at / updated_at timestamps (optional but helpful)
  • Add indexes on:
    • airtable_record_id
    • foreign keys you know you’ll query heavily
Why it matters: if the database can enforce uniqueness, your sync can retry safely.

Phase 2 — Sync path #1: New rows (Airtable → Supabase)

Option A: Zapier-style (fast to ship)

Use Zapier’s Airtable “New Record” trigger and send a webhook to Supabase (REST) or an Edge Function that inserts.
Implementation checklist:
Airtable trigger: New record in table
Transform: map Airtable fields → Supabase columns
Insert: use UPSERT semantics (not plain insert)

Option B: Script-based (more control)

Poll Airtable’s API (or use webhooks if available for your plan) and write inserts with retries + logging.
Implementation checklist:
Persist “last successful sync cursor”
Write idempotent insert logic (safe to re-run)
Log per-record success/failure for replay

Phase 3 — Sync path #2: Updates (Airtable → Supabase)

New rows are the easy part. Updates are where ops break.
Implementation checklist:
Choose update trigger strategy
  • Zapier: “Updated record” (careful: can fire for formula changes too)
  • Script: query updated records since last cursor
Make updates idempotent
  • Update by airtable_record_id
  • Prefer UPSERT over “update then insert” branching
Protect against partial updates
  • If your sync doesn’t always send all fields, decide whether missing fields mean “no change” or “clear it”

Phase 4 — Sync path #3: Historical backfill (one-time migration)

Do not backfill first. Backfill after new + update pipelines are stable.
Implementation checklist:
Freeze schema changes during backfill
Backfill table-by-table (or in dependency order)
Batch writes (e.g., 500–5,000 rows at a time) to avoid timeouts
Verify counts + spot-check records
Re-run backfill safely (UPSERT makes this possible)

Duplication prevention patterns that actually work

1) Use Airtable’s record ID as the durable key

Store it and enforce uniqueness:
  • airtable_record_id is your source-of-truth key
  • Every write uses it to decide “insert vs update”

2) Use database-level uniqueness + UPSERT

In Postgres terms, you want INSERT ... ON CONFLICT ... DO UPDATE.
In Supabase client terms, you want .upsert(..., { onConflict: 'airtable_record_id' }) and a unique constraint in the table.

3) Treat retries as normal

Zapier retries. Scripts crash. Webhooks duplicate. Assume it’ll happen and design for it.

When to migrate computed fields (rollups/lookups/formulas)

Airtable computed fields are derived, not raw truth.
Do this after the raw pipeline is stable:
  • Recreate formulas as SQL views
  • Recreate rollups with SQL aggregations + views/materialized views
  • Recreate lookups via joins
A practical checkpoint:
  • If “new rows” + “updates” have run cleanly for 7–14 days with no duplication incidents → start Phase 2 for computed fields.

Zapier vs Script: which should you pick?

Pick Zapier when:
  • You need it working this week
  • Volumes are low to moderate
  • You can tolerate occasional delay/retry behavior
Pick a script/worker when:
  • You need strong observability + replay
  • You have high volume or many tables
  • You need custom logic (transformations, denormalization, ordering)
Many teams do both:
1) Start with Zapier for speed
2) Replace with scripts once the schema stabilizes

Suggested implementation order (fastest path to “not broken”)

  1. Raw tables + constraints in Supabase
  2. New rows sync (ship it)
  3. Updates sync (stabilize it)
  4. Historical backfill (migrate old data)
  5. Relations cleanup + normalization
  6. Computed fields via SQL (views/rollups)

Get help building this

An Airtable to Supabase sync usually breaks at the same two points: the first time a webhook retries and creates a duplicate, and when backfill runs against a schema that wasn’t built for it. If you’ve hit that wall, book a ZoomFlow session — one of our consultants can walk through your schema, set up the UPSERT constraints, and get your sync running cleanly in a single call.