Cloud
Migration

How to Migrate a Flutter App from Firebase to Supabase

How to migrate Flutter App Firebase to Supabase

If you’re planning to migrate a Flutter app from Firebase to Supabase, the short version is this: you’re not swapping one backend for a similar one, you’re switching data paradigms entirely — from Firestore’s document-and-collection model to a relational Postgres database, from Firebase’s proprietary security rules to Postgres Row Level Security, and from four separate Firebase SDKs to a single supabase_flutter package. Done carelessly, that’s a rewrite. Done deliberately, in the order this guide lays out, it’s a well-scoped engineering project most teams complete in a few weeks without users ever noticing a cutover happened.

This is the exact process — schema design, authentication, data, storage, real-time listeners, Cloud Functions, and a zero-downtime rollout — that a real migration follows end to end. Where the underlying work is a data-architecture and cloud-migration problem rather than a Flutter UI problem, it’s also the part of this project our database migration engineers get pulled into most often, since getting the Postgres schema and security model right the first time is what determines whether the rest of the migration goes smoothly.

Why Flutter Teams Are Migrating from Firebase to Supabase

Firestore’s free Spark tier covers 50,000 reads, 20,000 writes, and 20,000 deletes a day, and the pay-as-you-go Blaze plan bills at roughly $0.06 per 100,000 reads and $0.18 per 100,000 writes — cheap in isolation, but with no hard spending cap, a single unbounded query or a looping Cloud Function can turn a predictable bill into a five-figure surprise overnight. That’s the trigger most teams describe first, but it’s rarely the only reason a migration gets greenlit. The others tend to be architectural: Firestore has no server-side JOINs, so any query spanning more than one collection means either denormalizing data (and keeping every copy in sync by hand) or fetching documents client-side and stitching them together in Dart — a pattern that gets painful fast once an app has more than a handful of related entities.

Postgres, by contrast, is a relational database that Flutter teams already know how to model — and it isn’t a niche choice. PostgreSQL is now the most-used database among professional developers worldwide, according to the 2025 Stack Overflow Developer Survey, and Supabase’s growth reflects the same shift: the company raised a $500M round at a $10.5B valuation in mid-2026, with over 9 million registered developers building on its Postgres-based platform. For a Flutter team, the practical upside is a single Postgres database with real foreign keys, SQL views, and transactions, plus Supabase’s own Auth, Storage, Realtime, and Edge Functions layered directly on top of it — replacing Firebase’s four separate products with one coherent stack.

The most common triggers for this migration, in order: unpredictable Blaze billing at scale, the need for relational queries Firestore can’t express natively, wanting an open-source stack without vendor lock-in, and — increasingly — teams that started on Firebase for speed during an MVP and have now outgrown its data model as the app’s schema got more interconnected.

Firebase vs. Supabase: What Actually Changes for a Flutter App

Every Firebase product a typical Flutter app depends on has a Supabase equivalent — except one. Mapping them out before you touch any code is what turns “migrate the backend” into a concrete, step-by-step checklist:

Firebase ProductSupabase EquivalentWhat Changes
FirestorePostgres databaseDocument/collection model → relational tables with foreign keys; requires real schema design
Firebase AuthenticationSupabase Auth (GoTrue)Users move to the auth.users table; social/OAuth providers reconfigured; passwords need a migration strategy
Firebase StorageSupabase StorageFiles re-uploaded to Supabase buckets; access rules rewritten as Storage policies
Cloud Firestore security rulesRow Level Security (RLS)Rules become SQL policies enforced directly by Postgres, not a separate rules engine
Cloud FunctionsEdge Functions (Deno)Node.js functions rewritten in TypeScript/Deno; triggers reconfigured via Database Webhooks
Firestore real-time listenersSupabase Realtime.snapshots() streams become Postgres change subscriptions on specific tables
Firebase Cloud Messaging (FCM)No native equivalentMost teams keep Firebase (free tier) solely for push notifications alongside Supabase for everything else
Firebase vs. Supabase
Firebase vs. Supabase

Before You Migrate: Audit Your Firebase Project

A migration that starts with an honest inventory of the current project goes faster than one that starts with code. Before writing a single line of Postgres schema, confirm:

  • Every Firestore collection and its document shape — including collections used by only one screen, which are easy to forget until users hit a broken feature post-launch.
  • Every Firebase Auth provider in use — email/password, Google, Apple, phone auth — since each has a different migration path and some (phone auth in particular) require extra planning.
  • Current Firestore security rules, translated mentally into “who can read/write what” — this becomes your Row Level Security policy spec.
  • Every Cloud Function, what triggers it (HTTP call, Firestore write, scheduled job), and what it actually does.
  • Storage bucket structure and access patterns — public files vs. user-scoped private files need different Supabase Storage policies.
  • Anything depending on Firebase Cloud Messaging, since that’s the one piece with no direct Supabase replacement.

The Migration Process, Step by Step

With the audit done, the migration itself follows a consistent sequence. Each step builds on the previous one, and skipping the order — schema before data, data before code changes — is the single most common cause of a migration that drags on far longer than planned.

Step 1: Design your Postgres schema from your Firestore collections

This is the step that determines how smoothly everything after it goes. For each Firestore collection, decide whether it becomes a single Postgres table or splits into several normalized tables — a Firestore document with a nested array of items (like an order with line items) typically becomes a parent table plus a related child table joined by a foreign key, rather than one table with a JSON column holding the array. Gart’s own case study on moving a production e-commerce workload from MongoDB to a relational database covers this exact document-to-relational modeling exercise; the shift from Firestore to Postgres follows the same logic, since both are document-oriented NoSQL stores being replaced by a relational schema. Our broader comparison of document versus relational databases is a useful reference if your team is still deciding how far to normalize.

Step 2: Set up your Supabase project and Row Level Security

Create the Supabase project, apply your schema via SQL migrations (kept in version control from day one — this is not optional for a production app), and translate every Firestore security rule into a Postgres RLS policy. RLS runs inside Postgres itself rather than as a separate rules layer, which means policies are testable with plain SQL and enforced no matter which client — Flutter app, Edge Function, or an admin script — touches the table.

-- Example: users can only read their own rows
create policy "Users can view own profile"
on profiles for select
using ( auth.uid() = user_id );

Step 3: Migrate Firebase Authentication users

Supabase publishes an official set of open-source migration tools for exactly this handoff: firestoreusers2json exports every Firebase Auth user to a JSON file, and import_users loads that file directly into Supabase’s auth.users table. Existing password hashes migrate too, provided you export Firebase’s scrypt hash parameters (signer key, salt separator, rounds) from the console first — done correctly, users log in with their existing password on Supabase without a forced reset. Social/OAuth providers (Google, Apple) need to be reconfigured as separate Supabase Auth providers, since the underlying provider credentials don’t transfer automatically.

Step 4: Migrate Firestore data to Postgres

The same open-source toolchain handles data: firestore2json dumps a Firestore collection to a flattened JSON file, and json2supabase loads it into the Postgres table you designed in Step 1. For collections that need to split into multiple related tables rather than one flat table, the tooling supports custom “hooks” — small scripts that reshape a document into several output records before import. Run this against a staging Supabase project first and diff row counts against Firestore before touching production data.

Step 5: Migrate Firebase Storage files

File migration is a two-step download-then-upload process: files come out of the Firebase Storage bucket to a local filesystem, then go up into a Supabase Storage bucket. Set bucket-level access policies (public vs. authenticated-only) before the first user hits the app post-cutover — new Supabase Storage buckets default to private, unlike some Firebase Storage configurations teams may have loosened over time.

Step 6: Replace Cloud Functions with Edge Functions

Cloud Functions triggered by HTTP calls port over conceptually unchanged — an Edge Function is still a serverless function behind a URL, just written in TypeScript on Deno instead of Node.js. Functions triggered by a Firestore write need more thought, since Supabase’s equivalent is a Database Webhook that fires on a Postgres insert/update/delete and calls an Edge Function — the trigger model is push-based via webhook rather than an SDK-level `onCreate`/`onUpdate` listener, so this is usually the part of the migration that takes the most debugging time.

Step 7: Decide what happens to push notifications

This is the one gap in the comparison table above: Supabase has no built-in equivalent to Firebase Cloud Messaging. The typical pattern is a hybrid setup — Supabase for the database, auth, storage, and business logic, with Firebase kept solely for FCM, called from a Supabase Edge Function when a relevant database row changes. It’s not an elegant answer, but it’s the one nearly every team ends up with, and it’s worth deciding on explicitly rather than discovering it mid-migration.

Step 8: Dual-run, test, and cut over with zero downtime

Ship a version of the app that can read from Supabase while Firebase still holds the source of truth, verify parity on real user accounts in a staged rollout, then flip writes over and decommission Firebase last — not first. A CI/CD pipeline that can deploy the Supabase-pointing build to a percentage of users, combined with feature flags around the data layer, turns the cutover into a controlled rollout instead of a single risky release. This is also the point where SRE practices — monitoring query latency, error rates, and auth success rates on both backends side by side — catch schema or RLS mistakes before they reach every user.

Updating Your Flutter Code and Dependencies

Firebase’s Flutter integration is spread across several packages; Supabase’s is not. Swapping pubspec.yaml dependencies is the visible part of the migration, even though it’s the smallest part of the actual work:

Before (Firebase)After (Supabase)
firebase_coresupabase_flutter (single package)
cloud_firestore
firebase_auth
firebase_storage
Updating Your Flutter Code and Dependencies

Query syntax changes from Firestore’s document-and-collection API to Postgres-style filtering, and real-time listeners move from .snapshots() to Supabase’s stream API:

// Firestore: real-time query
FirebaseFirestore.instance
  .collection('posts')
  .where('userId', isEqualTo: uid)
  .snapshots();

// Supabase: equivalent real-time query
supabase
  .from('posts')
  .stream(primaryKey: ['id'])
  .eq('user_id', uid);

The shape of the code stays recognizable — a filtered, real-time stream of rows either way — which is why most teams find the client-side rewrite faster than the backend migration itself once the schema and RLS policies are in place.

Common Migration Mistakes to Avoid

  • Copying the Firestore data model into Postgres as JSONB columns. It “works” on day one and defeats the entire point of moving to a relational database — you lose joins, foreign key integrity, and most of the query flexibility that motivated the migration.
  • Writing RLS policies after launch instead of before. A table with no RLS policy and RLS enabled blocks all access by default; a table with RLS disabled is wide open. Both are easy to get backwards under deadline pressure — test every policy against a non-owner user before cutover, not after.
  • Forgetting phone-auth and MFA users during the Auth migration. Email/password and OAuth users move cleanly with the standard tooling; phone-verified and multi-factor accounts often need a custom migration path that’s easy to miss during planning.
  • Decommissioning Firebase before the dual-run period proves out. Keep both backends live and Firebase billing active until Supabase has handled real production traffic for at least one full billing cycle.
  • Underestimating the Cloud Functions rewrite. HTTP-triggered functions port over almost directly; Firestore-triggered functions rebuilt around Database Webhooks are a different execution model and consistently take longer than teams budget for.

How Long Does It Take, and What Does It Cost?

For a small-to-mid-size Flutter app (a handful of Firestore collections, standard email/OAuth auth, a few Cloud Functions), a careful migration following the steps above commonly runs two to six weeks for a small team working part-time on it, or one to two weeks of focused effort for a dedicated pair of engineers. Apps with heavily denormalized Firestore data, custom claims-based security rules, or many Firestore-triggered Cloud Functions push toward the longer end of that range, since schema redesign and Edge Function rewrites — not the mechanical data export — are what actually consume the time. On the cost side, the calculation that usually justifies the project isn’t the migration effort itself; it’s comparing that one-time cost against an uncapped Blaze bill that’s already trending upward month over month, which is the same comparison our cloud migration engagements run for any workload moving off a metered, per-operation billing model.

Planning a Firebase-to-Supabase migration for a production Flutter app?

Gart Solutions handles the database and cloud engineering side of backend migrations end to end — Postgres schema design from your existing NoSQL data, Row Level Security policy modeling, and a zero-downtime cutover plan — so your Flutter team can focus on the app instead of the migration mechanics.

10+ Years in Cloud & DevOps
50+ Enterprise clients served
4.9★ Clutch rating
Database Migration Cloud Migration DevOps & CI/CD SRE & Reliability Fractional CTO
Talk to a Gart Engineer →
Roman Burdiuzha

Roman Burdiuzha

Co-founder & CTO, Gart Solutions · Cloud Architecture Expert

Roman has 15+ years of experience in DevOps and cloud architecture, with prior leadership roles at SoftServe and lifecell Ukraine. He co-founded Gart Solutions, where he leads cloud transformation and infrastructure modernization engagements across Europe and North America. In one recent client engagement, Gart reduced infrastructure waste by 38% through consolidating idle resources and introducing usage-aware automation. Read more on Startup Weekly.

FAQ

What is the difference between Firebase and Supabase for a Flutter app?

Firebase is a proprietary Google platform built around Firestore, a NoSQL document database, plus separate Auth, Storage, and Cloud Functions products. Supabase is an open-source alternative built around a standard Postgres database, with Auth, Storage, Realtime, and Edge Functions layered directly on top of it. For a Flutter app, the practical difference is document-model queries versus SQL, and four separate Firebase SDKs versus one supabase_flutter package.

Why are Flutter developers migrating from Firebase to Supabase?

The most common reasons are unpredictable Firestore/Blaze billing at scale (Firebase has no hard spending cap), the lack of native relational queries in Firestore once an app's data model gets more interconnected, and a preference for an open-source stack built on Postgres — now the most-used database among professional developers.

How do you migrate Firestore data to Supabase Postgres?

Design a normalized Postgres schema from your Firestore collections first, then use Supabase's open-source firebase-to-supabase tooling: firestore2json exports a collection to a flattened JSON file, and json2supabase imports it into the matching Postgres table. Collections that need to split into multiple related tables use custom hooks to reshape the data during export.

Can you migrate Firebase Authentication users without forcing a password reset?

Yes, in most cases. Supabase's import_users tool can carry over existing password hashes if you export Firebase's scrypt hash parameters (signer key, salt separator, rounds, memory cost) from the Firebase console before migrating. Social/OAuth provider logins (Google, Apple) need to be reconfigured separately, since provider credentials aren't part of the password hash migration.

Do you need to rewrite all your Flutter code to switch to Supabase?

You need to replace Firebase's SDK calls with Supabase's client library calls throughout the app — that part is unavoidable — but the underlying UI and app architecture typically stay the same. In practice, four Firebase packages (firebase_core, cloud_firestore, firebase_auth, firebase_storage) are replaced by one package, supabase_flutter, and most teams find the client-side rewrite faster than the backend data migration itself.

How long does a Firebase-to-Supabase migration take for a Flutter app?

A small-to-mid-size app commonly takes two to six weeks part-time, or one to two weeks of focused work for a dedicated pair of engineers. Apps with heavily denormalized Firestore data, custom security rules, or many Firestore-triggered Cloud Functions take longer, since schema redesign and function rewrites — not the mechanical data export — consume most of the time.

Should you migrate gradually or cut over all at once?

Gradually. The standard pattern is a dual-run period — the app can read from Supabase while Firebase still holds the source of truth — followed by a staged rollout that flips writes over to Supabase and only decommissions Firebase after Supabase has handled real production traffic through at least one full billing cycle. A single hard cutover with no fallback is the highest-risk way to run this migration.
arrow arrow

Thank you
for contacting us!

Please, check your email

arrow arrow

Thank you

You've been subscribed

We use cookies to enhance your browsing experience. By clicking "Accept," you consent to the use of cookies. To learn more, read our Privacy Policy