I get asked some version of "which database should I use" often enough that it's worth writing down properly. The honest answer is that it depends on the shape of the project, not on which tool is objectively "best" — I've shipped production work on all three, and each one was the right call for its specific project.

Plain PostgreSQL, self-managed or via a managed host

This is still my default for anything with a Node.js or dedicated backend where I want full control over the schema, query performance, and hosting. It's also what I reach for on anything with genuinely complex relational data or reporting needs, where I want to write real SQL rather than work through an abstraction layer. FactoryBuyo's inventory system, with its spec comparisons and filtering, is a good example — the queries needed enough control that an abstraction would have gotten in the way.

If the project needs complex joins, reporting, or fine-grained query tuning, I reach for plain PostgreSQL before anything else.

Supabase, when I want Postgres with batteries included

Supabase is PostgreSQL underneath, which means I get real SQL and relational integrity, but with auth, row-level security, real-time subscriptions, and storage handled for me. This is my choice when a project needs real user accounts and role-based access — LeaflandKerala's employee login and district-level data is a direct example — but doesn't justify building a custom backend from scratch. Row-level security in particular has saved me from writing a lot of authorization boilerplate by hand.

supabase-policy.sqlsql
create policy "users can view own trades"
on trades for select
using (auth.uid() = user_id);

Convex, when the app is highly interactive and reactive by nature

Convex earns its place when an application's core value is real-time, reactive state shared across a UI — dashboards, collaborative tools, anything where "the data on screen should always be current" is a hard requirement rather than a nice-to-have. Its function-based backend model and automatic reactivity remove a lot of the plumbing I'd otherwise hand-write with websockets or polling. The tradeoff is that it's a more opinionated, TypeScript-native model than raw SQL, so it fits some problems better than others.

Best fitTradeoff
Plain PostgreSQLComplex relational data, custom backend, full controlYou own auth, real-time, and infra decisions yourself
SupabaseReal SQL plus auth, storage, and real-time out of the boxLess control than raw Postgres hosting, though it's real Postgres underneath
ConvexHighly reactive, real-time-by-default applicationsMore opinionated model; less natural fit for heavy relational/reporting work

The question I actually ask

Instead of "which database is best," I ask: does this project need raw relational power and full control (Postgres), does it need auth and real-time with minimal setup on top of real SQL (Supabase), or is the product fundamentally about reactive, shared state (Convex)? Answering that honestly, before writing any backend code, has saved me from re-platforming more than once.

What I'd avoid

The mistake I see most often — and made myself early on — is picking a database because it's trendy or because a tutorial made it look effortless, without checking whether its actual strengths match the project's actual shape. A reactive, document-flavored backend is a poor fit for an application that's fundamentally about financial reporting and complex joins, no matter how pleasant its developer experience is for other use cases.