DEV Community

Cover image for Migrating a Side Project from Firebase to PocketBase Cloud: A Practical Guide
tienbuilds
tienbuilds

Posted on

Migrating a Side Project from Firebase to PocketBase Cloud: A Practical Guide

Firebase is a fantastic place to start a project and an awkward place to grow one. The vendor lock-in is real, the pay-as-you-go bill gets hard to predict once you have traffic, and you can't take Firestore with you when you leave.

I moved a small app off Firebase and onto PocketBase Cloud recently, and it was smoother than I expected. This is the practical version of how to do it — the concept mapping, the code changes, the data migration, and an honest note on when you should just stay on Firebase.

Disclosure: I work on PocketBase Cloud. I've tried to keep this a real migration guide rather than a sales pitch — including a section on where Firebase is still the better call.

Why migrate at all?

Not everyone should. But common reasons:

  • Predictable cost. Firebase bills per read/write/invocation. PocketBase Cloud bills per server — you know your number in advance, and it doesn't spike with a traffic burst.
  • No lock-in. PocketBase is open source and self-hostable. Your data lives in a SQLite file you can download and run anywhere. Cloud hosting is a convenience, not a cage.
  • One system. Database, auth, file storage, and server-side logic in a single tool instead of four Firebase products stitched together.

Concept mapping

Most Firebase concepts have a direct PocketBase equivalent, which is what makes the move manageable:

Firebase PocketBase
Firestore collection Collection (with a real schema)
Firestore document Record
Firebase Auth Built-in auth (email/pw, OAuth2, OTP)
Security Rules API Rules (filter expressions per collection)
Cloud Storage File fields on records
Cloud Functions JS hooks (routes, events, cron)
onSnapshot() realtime subscribe() over SSE

The biggest mental shift: Firestore is schemaless, PocketBase collections have a schema. That feels like more work up front and saves you from a lot of "why is this field sometimes a string and sometimes null" later.

Step 1 — Deploy the backend

Sign up at PocketBase Cloud, deploy an instance (~30 seconds), and open the admin dashboard at /_/. Recreate your collections there, this time with proper field types and validation. Where Firebase made you enforce shape in Security Rules or client code, here the schema does it.

Step 2 — Swap the client SDK

Here's the before/after for the two operations you do most.

Reading a collection

// Firebase
import { collection, getDocs } from "firebase/firestore";
const snap = await getDocs(collection(db, "posts"));
const posts = snap.docs.map((d) => ({ id: d.id, ...d.data() }));

// PocketBase
import PocketBase from "pocketbase";
const pb = new PocketBase("https://your-app.pocketbasecloud.com");
const posts = await pb.collection("posts").getFullList({ sort: "-created" });
Enter fullscreen mode Exit fullscreen mode

Realtime subscription

// Firebase
import { onSnapshot, collection } from "firebase/firestore";
onSnapshot(collection(db, "messages"), (snap) => {
  snap.docChanges().forEach((c) => {
    if (c.type === "added") addMessage(c.doc.data());
  });
});

// PocketBase (SSE under the hood)
pb.collection("messages").subscribe("*", (e) => {
  if (e.action === "create") addMessage(e.record);
});
Enter fullscreen mode Exit fullscreen mode

The shapes are close enough that most of the migration is find-and-replace plus adjusting field names.

Step 3 — Move auth

Firebase Auth users don't export with password hashes in a reusable form, so the usual pattern is:

  1. Recreate your OAuth providers (Google, GitHub, etc.) in the PocketBase dashboard — these "just work" since the user re-consents on next login.
  2. For email/password users, trigger a password reset flow on first login after migration, so they set a new password against the new backend.

Permissions move from Security Rules to API Rules — short filter expressions on each collection:

// "only the owner can update their own record"
@request.auth.id != "" && owner = @request.auth.id
Enter fullscreen mode Exit fullscreen mode

If you've written Firestore Security Rules, this will feel familiar and considerably shorter.

Step 4 — Migrate the data

Export each Firestore collection to JSON (the Firebase CLI or a small Admin SDK script does this), then write records into PocketBase via the SDK:

import PocketBase from "pocketbase";
const pb = new PocketBase("https://your-app.pocketbasecloud.com");
await pb.admins.authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD);

const posts = JSON.parse(fs.readFileSync("posts.json", "utf-8"));
for (const p of posts) {
  await pb.collection("posts").create({
    title: p.title,
    body: p.body,
    // map Firestore fields → PocketBase fields here
  });
}
Enter fullscreen mode Exit fullscreen mode

For anything beyond a few thousand records, batch it and add a small delay so you don't hammer your instance. Files move the same way: download from Cloud Storage, upload as file fields.

Step 5 — Cloud Functions → hooks

Firebase Cloud Functions become PocketBase JS hooks, running inside your instance next to the data. A Firestore onCreate trigger becomes an onRecordAfterCreateSuccess hook; a scheduled function becomes a cron hook. The nice difference: hooks aren't billed per invocation, so a hot code path doesn't translate into a scary bill.

If you need heavier server-side code than hooks allow — long-running jobs, websockets, a full Node/Deno/Bun/Next.js service — the Pro plan runs those on your server too, so the whole app stays on one platform.

When you should stay on Firebase

Genuinely — don't migrate if:

  • You need massive write concurrency. PocketBase is SQLite; writes serialize. Firestore scales horizontal writes in ways SQLite doesn't. A high-write, high-concurrency workload is a real reason to stay.
  • You lean on the wider Firebase/GCP ecosystem — FCM push, ML Kit, BigQuery pipelines, Crashlytics. PocketBase is a focused backend, not a cloud platform.
  • Offline-first mobile with automatic conflict resolution is core to your app. Firestore's offline persistence is more mature here.

For read-heavy CRUD apps — dashboards, content sites, most mobile backends — none of that bites, and you get a simpler, cheaper, portable stack.

Try it

The migration is easiest to feel by doing a throwaway one. The Free plan (1 instance + 5 frontends, stays running) is enough to recreate a collection or two and port a screen:

👉 pocketbasecloud.com

If you're mid-migration and hit something that doesn't map cleanly, drop it in the comments — I've been through a few of these and I'm happy to help work it out. 🚀

Top comments (0)