{
  "$type": "site.standard.document",
  "path": "/you-do-not-need-firebase-and-supabase/",
  "publishedAt": "2026-05-16T12:27:00.000Z",
  "site": "at://did:plc:bryys25pc2fnagnyxqgsglhd/site.standard.publication/3mn26bjkkmh23",
  "tags": [
    "AI",
    "Web"
  ],
  "textContent": "When I build a small SaaS that calls paid model APIs, I do not want to start by wiring together multiple backend platforms.\n\nMy take: I would not start with Firebase or Supabase.\n\nI would start with Postgres. Self-host it if you can. If not, use hosted Postgres from someone else, but keep it as normal Postgres that you can back up, move, and query directly.\n\nI WANT TO OWN THE DATABASE\n\nFor a solo SaaS, especially before the first 100 paying users, the backend should be boring.\n\nBoring does not mean weak. It means I know where the data is, where auth is checked, where billing limits are enforced, where paid provider usage is counted, and how I get the whole thing out if I need to move.\n\nThat is why I lean toward plain Postgres.\n\nThe app will change. I might switch frontend frameworks, AI providers, or payment providers. The database is the part I want to keep stable.\n\nI do not want my core product data modeled around a BaaS SDK.\n\nTHE PROBLEM WITH BAAS DEFAULTS\n\nFirebase and Supabase are useful products. I have used Firebase for prototypes before. Supabase gets you Postgres plus auth quickly.\n\nBut they both want to be more than a database.\n\nFirebase gives you Auth, Firestore, Storage, Functions, security rules, and client SDKs.\n\nSupabase gives you Auth, Postgres, Storage, Edge Functions, row-level security, realtime, and client SDKs.\n\nThat is convenient until the product depends on the platform more than the backend code.\n\nNow the decisions are not just:\n\n * what tables do I need?\n * what queries do I need?\n * what does the backend enforce?\n\nThey become:\n\n * which client SDK owns this flow?\n * are permissions in application code, security rules, or row-level security?\n * does the frontend write directly to the database?\n * how do I run the same setup locally?\n * how do I back this up?\n * how do I move this if pricing, limits, or product direction changes?\n\nFor a small app, that is too many platform decisions before the product has customers.\n\nPUT USAGE AND BILLING IN SQL\n\nFor an app that charges for paid provider usage, I want Postgres.\n\nThe product can look simple at first. Then you need:\n\n * saved configurations\n * versions\n * tags\n * owners\n * teams\n * permissions\n * model settings\n * variables\n * test inputs\n * generated outputs\n * usage counts\n * billing limits\n * audit logs\n\nThat maps well to relational data. Some parts are structured. Some parts are flexible. Postgres handles both.\n\nExample:\n\ncreate table ai_workflows (\n  id uuid primary key default gen_random_uuid(),\n  user_id uuid not null,\n  title text not null,\n  instructions text not null,\n  model text not null,\n  metadata jsonb not null default '{}',\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now()\n);\n\ncreate table ai_workflow_versions (\n  id uuid primary key default gen_random_uuid(),\n  workflow_id uuid not null references ai_workflows(id),\n  instructions text not null,\n  metadata jsonb not null default '{}',\n  created_at timestamptz not null default now()\n);\n\ncreate table ai_runs (\n  id uuid primary key default gen_random_uuid(),\n  workflow_id uuid not null references ai_workflows(id),\n  user_id uuid not null,\n  model text not null,\n  input jsonb not null default '{}',\n  output text,\n  tokens_input integer not null default 0,\n  tokens_output integer not null default 0,\n  created_at timestamptz not null default now()\n);\n\nThat is enough to start.\n\nYou can add team_id later. You can add sharing later. You can add full-text search later. You can add pgvector later if semantic search becomes useful.\n\nThe important thing is that the core data is in one place, in a format I can query directly.\n\nUSAGE HAS TO BE MEASURABLE\n\nCalls to OpenAI, Anthropic, Gemini, or any other paid provider cost money. That changes the backend design.\n\nIf your app lets users run paid provider calls, the database needs to answer basic operational questions:\n\nselect\n  user_id,\n  count(*) as runs,\n  sum(tokens_input + tokens_output) as total_tokens\nfrom ai_runs\nwhere created_at >= date_trunc('month', now())\ngroup by user_id\norder by total_tokens desc;\n\nI expect to write queries like this in any app that charges for paid provider usage.\n\nWho used the most tokens this month? Which workflow is expensive? Which free account is using too much? Did this customer exceed their plan? How many failed runs came from one model?\n\nThese are database questions.\n\nI could model this in Firebase. I just would not pick a document database for an app where I already know usage, billing, joins, and reporting matter.\n\nSELF-HOST POSTGRES IF YOU CAN\n\nMy preference:\n\n * self-host Postgres\n * if not, hosted Postgres\n * if not, a BaaS with Postgres underneath\n * Firebase only if the app genuinely fits Firebase\n\nSelf-hosting does not mean building a data center. For my current projects, one Hetzner server with Kamal gets me a lot of mileage.\n\nPostgres backups are straightforward. Restores are understandable. Logs are mine. Migrations are mine. The app connects over a normal database URL. No special integration needed.\n\nHosted Postgres is fine too. I care less about the brand than the basics:\n\n * can I connect with standard Postgres tools?\n * can I run migrations?\n * can I dump and restore?\n * can I move away without rewriting the app?\n * can my backend own the product rules?\n\nIf yes, that is enough.\n\nKEEP THE BACKEND IN CHARGE\n\nIf there is already a backend, I would keep the security model simple:\n\n * the frontend calls your backend\n * the backend checks the user\n * the backend reads and writes Postgres\n * the backend calls the model provider\n * the backend handles payment webhooks\n * the backend enforces free tier limits and paid plan limits\n\nDo not let the frontend become the place where important decisions happen.\n\nThe frontend can show the editor. It can show \"3 runs left.\" It can disable a button. But the backend has to enforce the limit.\n\nI also would not put model provider calls in the frontend. API keys stay server-side. Usage gets recorded server-side. Billing checks happen server-side.\n\nWHAT I WOULD BUILD FIRST\n\nFor a solo MVP, I would start with this:\n\n * frontend\n * one backend API\n * Postgres\n * one auth system\n * payment webhooks handled by the backend\n * model provider API called only from the backend\n * an ai_runs table for usage tracking from day one\n\nThe first version can be plain:\n\nfrontend -> backend -> Postgres\n                 |\n                 -> model provider\n                 |\n                 -> payment webhook handling\n\nThe backend owns the product rules.\n\nThat means:\n\n * who can read a workflow\n * who can edit a workflow\n * how many free runs are left\n * whether the account is paid\n * which model can be used\n * how much usage was recorded\n\nYou can still use a hosted auth provider if you want. You can still use a managed Postgres provider if you do not want to run the database yourself.\n\nThe line I would avoid crossing early: making the app depend on a BaaS when a normal backend plus Postgres is enough.\n\nTHE STACK SHOULD MATCH THE STAGE\n\nBefore 100 paying users, the stack has one job: help you learn what people will pay for.\n\nYou need enough structure that the app can grow. You do not need to organize the backend around a BaaS before you know what the product needs.\n\nFor this product, I would use Postgres and keep moving. Self-hosted if practical. Hosted if that saves time. Either way, I want standard Postgres, normal migrations, normal backups, and backend code that owns the business rules.\n\nFirebase and Supabase can both work. I just would not start there by default.",
  "title": "You Do Not Need Firebase and Supabase"
}