Skip to main content
ASoc
Comparison

Vercel vs. Firebase: Two Route Handlers Instead of a Function per Endpoint

This storefront's whole backend is 2 Route Handlers, 8 Server Actions and a 42-line Edge proxy — no Cloud Functions, no separate BaaS deploy.

The ASoc Team8 min read

Vercel hosts a framework; Firebase is a backend-as-a-service with its own database, auth and functions runtime. Most comparisons resolve that mismatch by calling them "complementary" and stopping there. This storefront runs on Vercel and answers a sharper question: with Next.js Route Handlers and Server Actions doing the work, what would Firebase's Cloud Functions actually replace? Here, exactly two Route Handlers and eight Server Action modules — not a function per endpoint.

The short answer

Vercel is a hosting and compute platform built around a framework; it ships no database, no auth SDK, and no functions runtime of its own — you bring those. Firebase is the opposite shape: a bundled backend (Firestore, Auth, Cloud Functions) that expects you to write against its SDKs. Pick Vercel when a framework's own primitives — Route Handlers, Server Actions, Edge middleware — already cover your backend surface. Pick Firebase when you want a database, auth and callable functions provisioned together with no schema to design.

What each platform actually is

Vercel (this storefront)Firebase
What it hostsA Next.js app: pages, Route Handlers, Server Actions, an Edge proxyA static frontend (Firebase Hosting) plus a backend (Firestore/Realtime DB, Auth, Storage)
Backend surface2 Route Handlers, 8 Server Action modules, all in one deploymentEach Cloud Function is typically its own deployment unit, invoked by HTTPS, a Firestore trigger, or Pub/Sub
DatabaseNot included — this app brings Postgres via SupabaseFirestore or Realtime Database, bundled
AuthNot included — this app brings Supabase Auth, refreshed by a 42-line Edge proxy on every requestFirebase Authentication, SDK-managed, its own emulator and console
Background workafter(), backed by Vercel's waitUntil(), keeps a fire-and-forget promise alive past the responseCloud Functions triggers, or Cloud Tasks for work deferred past the request
Env varsProject-level; rebuild vs. restart semantics decide whether a change is live (see Next.js deployment)Set per-function; Gen 2 functions read .env files bundled at deploy time
Pricing shapeUsage-based compute, Pro from $20/moFreemium BaaS — a generous free tier, then pay per read/write/invocation

The pricing row is the one most comparisons stop at, and it is the least useful without a build to measure against — Firebase Hosting cost against this repository's actual 156.5 MB output runs that arithmetic and finds storage is free at this scale while transfer is the line that bills.

The row worth sitting with is backend surface. Firebase's unit of backend logic is the Cloud Function: one deployable, one cold start, one set of triggers. This app's backend logic is two Route Handlers and eight Server Action files, and none of them is a separate deployment — they ship inside the same Next.js build that renders the pages.

What this storefront's backend actually is

Counted in this checkout, not estimated:

src/app/api/download/route.ts
src/app/api/webhooks/lemonsqueezy/route.ts

src/lib/actions/account.ts
src/lib/actions/auth.ts
src/lib/actions/checkout.ts
src/lib/actions/contact.ts
src/lib/actions/entitlementsView.ts
src/lib/actions/newsletter.ts
src/lib/actions/redemption.ts
src/lib/actions/refund.ts

Two Route Handlers, eight Server Action modules, and one 42-line Edge proxy (src/proxy.ts) that runs ahead of nearly every request to refresh the auth session. On Firebase, the rough equivalent — a webhook receiver, a gated download endpoint, and eight callable mutations — would be ten separate Cloud Functions, each with its own cold-start profile and its own entry in the Firebase console. Here it is ten files in one Next.js app, deployed as one unit.

That is not automatically a win — a monolith you cannot scale a single hot path independently is a real tradeoff — but it is the concrete shape of "Vercel doesn't need a BaaS to have a backend," rather than the abstract claim.

The defect Firebase's model would not have caused this way

The one Route Handler with the least forgiving failure mode is the LemonSqueezy webhook — it sends a purchase-confirmation email after the entitlement transaction has already committed, so the email can never be allowed to affect the response:

// src/app/api/webhooks/lemonsqueezy/route.ts
export async function POST(req: Request) {
  const raw = await req.text();
  // ...verify signature, run processWebhook...

  return new Response(result.body, { status: result.status });
}

The email send is wired in as a callback:

// Scheduled via `after()` (next/server) rather than invoked directly:
// on Vercel a fire-and-forget promise that isn't awaited has no
// guaranteed lifetime once the response is sent — the invocation can
// freeze/terminate before the Resend call completes, silently dropping
// the email. `after()` is backed by Vercel's `waitUntil()`, which keeps
// the invocation alive for scheduled work without blocking the
// response itself (verified against next/dist/docs/.../after.md).
sendPurchaseWelcome: (email, tier) =>
  after(() => sendPurchaseWelcome(email, tier)),

That comment is describing a real, Vercel-specific gotcha: a serverless invocation on Vercel does not keep running just because you started a promise and didn't await it. The instant the Response goes out, the function can be frozen — and an un-awaited fetch() to Resend can lose the race and simply never complete. after() exists precisely to fix this, by handing the callback to Vercel's waitUntil(), which keeps the invocation alive for scheduled work without holding up the response.

Firebase Cloud Functions have a related but different version of the same constraint: a Gen 2 HTTPS function (backed by Cloud Run) also finishes its billable execution when the response is sent, and background work started inside the handler is not guaranteed to complete either — the documented fix there is Cloud Tasks or a separate triggered function, not a same-request callback. The failure mode is the same shape — "the platform doesn't owe your fire-and-forget code any time after you respond" — but the fix is platform-specific in both directions. Neither is more correct; treating either platform's request lifecycle as "just Node" is what breaks.

Auth and the site-wide risk that comes with bringing your own

Because Vercel doesn't bundle auth, this app owns it — Supabase Auth, refreshed on every request by src/proxy.ts. That file runs on a matcher covering almost the whole site, including pages with no login UI at all, and both of its Supabase env vars carry a non-null assertion. Ship this app to a Vercel project that hasn't set them, and the proxy throws on every request, marketing home page included — a failure mode CLAUDE.md warns about in bold. Firebase sidesteps this specific trap because its SDKs fail per-call rather than in a request-wide middleware layer, but it trades that for its own version: an unconfigured Firebase project fails individual getAuth()/getFirestore() calls at the point of use, which is easier to isolate but easier to miss in testing because unrelated pages keep working. The full accounting of this tradeoff — RLS policies, the service-role split, the bundle cost of the auth SDK — is in Vercel Postgres vs. Supabase; this post only needed the one failure mode that's specific to bringing an external auth provider onto Vercel's request lifecycle at all.

Mistakes and troubleshooting

SymptomCauseFix
A fire-and-forget email or log never arrives, only on VercelThe invocation froze once the response was sent; an un-awaited promise has no guaranteed lifetimeWrap it in after(), which is backed by waitUntil()
Every route 500s after deploying to a fresh Vercel projectAn Edge proxy/middleware reads an env var with a non-null assertion, and the project's env vars aren't set yetSet the vars before the first production deploy, not after the first 500
A Firebase Cloud Function is slow on the first request after a whileCold start — a new container has to boot for that functionBundle related logic into fewer functions, or configure minimum instances
"Where do I put a background job" on VercelThere's no built-in equivalent to a Firestore-triggered functionUse Vercel Cron Jobs for scheduled work, or a Route Handler triggered by an external webhook/queue
Confusing the Edge proxy with a Route HandlerBoth run server-side, but the proxy is matcher-scoped and meant for cross-cutting concerns, not endpoint logicKeep actual endpoints in Route Handlers; use the proxy only for things every matched request needs

Frequently asked questions

Can I deploy Firebase Cloud Functions to Vercel? No. Cloud Functions are Google Cloud-specific — they run on Google's infrastructure and are triggered through Firebase's own event system. Porting one to Vercel means rewriting it as a Route Handler or Server Action; the code that touches Firestore or Firebase Auth goes away entirely if you also drop those services.

Do I still need Firebase if I'm hosting on Vercel? Not necessarily. Vercel's Route Handlers and Server Actions cover a real amount of what a BaaS's functions layer does, as this app's two Route Handlers and eight Server Actions show. What Vercel does not give you is a database or an identity provider — you still need one of those from somewhere, whether that's Firebase, Supabase, or a database you provision separately.

Is Vercel a Backend-as-a-Service like Firebase? No. Vercel is a hosting and compute platform for frontend frameworks. It has no bundled database, no auth SDK, and no document store. Everything in that category — Postgres, Supabase, Firebase, or otherwise — is something you add.

Can I use Firebase's database with a Vercel-hosted app? Yes, and it's a common pairing: Firebase supplies Firestore/Auth, Vercel hosts and renders the Next.js app that calls them. It's the same "hosting platform plus a chosen backend" shape this storefront uses with Supabase instead.

Templates in this post

ASoc Sentinel is a security-suite landing page leading with a live protection-status hero — threats blocked, devices covered, VPN status — plus a monthly/yearly pricing table. ASoc Signal markets an AI voice-and-image platform: voice cloning, script-to-voiceover generation, and a six-tool image studio behind a dashboard mock. ASoc Sterling is a wealth-management marketing site pairing a total-balance dashboard preview with advisor-reviewed portfolio management and tax-smart investing messaging.

Browse the full sets: Next.js landing page templates, Tailwind landing page templates.

Keep reading

Comparison8 min read

Vercel vs. GitHub Pages: 344 Static Pages, 8 That Need a Server

GitHub Pages has no compute layer at all — not slower, absent. A fresh build of this storefront counts exactly which routes that categorically rules out, and why it isn't a rendering-mode question.

Read more
Comparison10 min read

Vercel vs. Render: Does Anything You Run Need to Stay Running?

Two Route Handlers, both stateless, neither overriding the default timeout — versus a release script that clones, installs, and boots for minutes. That split is the real decision axis.

Read more
Comparison8 min read

Vite vs. Vue: A Build Tool and a Framework Are Not Alternatives

Vite builds, Vue renders, and most projects use both. The comparison that resolves is Vite vs. Vue CLI — plus which layer to blame when a build breaks.

Read more