Skip to content
← Blog
Technicalby Goodspeed Team

Why We Chose Supabase for Mobile App Backends

After building 10+ mobile apps on Supabase, here is what works, what does not, and why we chose it over Firebase and custom backends.

Every mobile app needs a backend. Authentication, database, file storage, serverless functions. The question is whether to build your own, use Firebase, or try one of the newer options. After building more than 10 apps on Supabase, we can share what the experience is actually like.

We chose Supabase for Goodspeed generated apps because it hits the sweet spot between developer control and managed convenience. But it is not perfect. Here is the honest breakdown.

## What Supabase gets right

### PostgreSQL underneath

This is the biggest differentiator. Supabase is not a proprietary database. It is PostgreSQL with a nice API layer on top. That means you get real SQL queries, proper relational data modeling, foreign keys, indexes, views, and stored procedures.

When we generate apps, the database schema is a standard SQL migration file. If a builder ever wants to move off Supabase, they take their migration files to any Postgres host and run them. No data lock-in. No proprietary query language to translate.

Compare this to Firebase Firestore, where your data is stored in a document model that only makes sense within the Firebase ecosystem. Migrating off Firestore means rewriting your data model and every query that touches it.

### Row-Level Security

RLS is Postgres-native access control at the database level. Instead of writing access checks in your application code (and hoping you did not miss one), you define policies directly on the database tables. "Users can only read their own data" becomes a one-line policy that applies to every query, every API call, every edge function.

For generated apps, this is critical. We define RLS policies in the migration files alongside the schema. When the app is deployed, the security model is enforced at the database level. Even if the application code has bugs, the database will not return data the user should not see.

### Auth that just works

Supabase Auth handles email/password, OAuth (Google, Apple, Twitter/X, GitHub), magic links, and phone authentication. The client SDK provides signUp, signInWithPassword, signInWithOAuth, and signOut methods that work across React Native and web.

We have used Supabase Auth across every app we build. The experience is consistent. Email verification works. OAuth callbacks work (though they require careful redirect URL configuration). Session management is automatic. Password reset flows are handled by hosted pages that you can customize.

The one pain point: configuring OAuth providers requires going into each provider developer console (Google Cloud Console, Apple Developer Portal, Twitter Developer Portal) and setting up credentials. This is not a Supabase problem. It is an OAuth problem. But it is still tedious.

### Edge Functions

Supabase Edge Functions are Deno-based serverless functions that run close to your database. They are useful for operations that should not happen on the client: payment verification, external API calls, data aggregation, and scheduled tasks.

We use edge functions for things like completing onboarding (a multi-step database operation that should be atomic) and processing webhooks from external services. The cold start time is fast (usually under 100ms), and the Deno runtime supports TypeScript natively.

## What Supabase gets wrong (or at least makes hard)

### Null handling

Supabase returns null for columns without values. This sounds obvious, but TypeScript types generated from the schema say number or string, not number | null. If you write .toFixed(2) on a database value without null-guarding, your app crashes at runtime.

We learned this the hard way. Our generated apps now include null guards on every numeric value from the database: (value ?? 0).toFixed(2). It is tedious but necessary. This is a TypeScript tooling issue as much as a Supabase issue, but it bites you in production.

### Real-time subscription cleanup

Supabase real-time subscriptions are powerful but require careful cleanup. If you subscribe to a table in a React component and do not unsubscribe when the component unmounts, you leak subscriptions. Enough leaked subscriptions and the app slows down.

Our generated apps handle this with cleanup functions in useEffect hooks, but it is a common source of bugs for developers building manually.

### Local development story

The Supabase local development setup (using Docker) works but is heavier than alternatives. Starting the local stack requires Docker Desktop running, and the initial pull downloads several gigabytes of images. For quick iteration, it is faster to develop against a remote Supabase project and use local development only for migration testing.

## Supabase vs Firebase: the practical comparison

| Feature | Supabase | Firebase | |---|---|---| | Database | PostgreSQL (relational) | Firestore (document) | | Query language | SQL | Custom SDK methods | | Access control | Row-Level Security | Security Rules (custom DSL) | | Auth | Built-in, standard OAuth | Built-in, strong Google integration | | File storage | S3-compatible | GCS-based | | Functions | Deno Edge Functions | Cloud Functions (Node.js) | | Pricing | Generous free tier, predictable scaling | Free tier, pay-per-operation scaling | | Vendor lock-in | Low (PostgreSQL is portable) | High (proprietary data model) | | Offline support | Limited (community solutions) | Built-in (Firestore offline cache) |

Firebase wins on offline support and Google ecosystem integration. Supabase wins on data portability, SQL flexibility, and pricing predictability.

For our use case (generating apps that builders own and can modify), Supabase portability is the deciding factor. We cannot generate an app on a proprietary platform and then tell the builder "you own this code." Owning code that is tightly coupled to Firebase is not real ownership.

## Patterns we use in every app

### CamelCase to snake_case conversion

JavaScript uses camelCase. PostgreSQL conventions use snake_case. Every object that goes to or comes from the database needs conversion. We use toDb() and fromDb() utility functions that handle this mapping automatically.

### The service key pattern

When calling Supabase REST APIs from server-side code, you need to pass the service role key in both the apikey header and the Authorization header. Missing either one causes authentication failures that are hard to debug. This pattern is now baked into our generated service layers.

### Migration-first development

Every database change starts as a SQL migration file. No manual dashboard edits. No ad-hoc queries. The migration files are the source of truth for the database schema. This makes deployments repeatable and rollbacks possible.

## Should you use Supabase?

If you are building a [mobile app](/features/building) or web app that needs auth, a database, and basic serverless functions, Supabase is an excellent choice. The free tier is generous (500MB database, 1GB file storage, 50K monthly active users), and scaling costs are predictable.

If you need heavy offline support, real-time multiplayer features, or deep integration with Google ML/AI services, Firebase is worth considering. Both are solid platforms. Choose based on your specific requirements.

For Goodspeed apps, Supabase is our default backend. It gives every generated app a production-ready backend with proper security, standard SQL, and no vendor lock-in. Explore our [technology stack](/features/building) to see how Supabase fits into the full build pipeline.

Ready to build?

Score your first idea free. See the pipeline in action.