Guides

jstash vs Firebase: when to use each

Firebase is a full app platform from Google. jstash Apps does one small thing: it keeps private JSON for each signed-in person. They overlap on one job, saving each person’s own data from a web app. Here’s how they compare on that job, and when to use each.

Written by jstash, which makes one of the options compared here. Updated 4 October 2026.

Summary

  • Queries, data people share, live updates, files, server code or mobile SDKs: use Firebase. jstash Apps doesn’t do any of these.
  • Already using Firestore: add an owner-only rule and stay there. It’s a few lines, and Firebase’s free tier is far bigger than jstash’s.
  • Each person’s own settings, progress or drafts, with no rules to write: jstash Apps. It works with Firebase Authentication, Clerk, Supabase or Google sign-in. Free covers 1 app and 100 users.
  • Both: you can keep Firebase Authentication for sign-in and keep each person’s JSON in jstash.

What each one is

Firebase is Google’s app platform. Its databases include Cloud Firestore and the Realtime Database, which apps read and write directly. Security rules you write decide who can read and write what. Around them are Authentication, Cloud Storage for files, Cloud Functions for server code, Hosting and more.

jstash Apps, which we make, keeps private JSON documents for each signed-in person of your app. Your page sends the login token it already has, and jstash checks it. There’s one access rule, built in: each person reaches only their own documents.

This page compares Firestore, with Firebase Authentication and security rules, against jstash Apps.

The same task, both ways

Save and load one signed-in person’s settings. Both versions assume Firebase Authentication already signs people in. In both, wait until sign-in has finished before you load: Firebase recommends an onAuthStateChanged observer for that.

With Firestore

You write two things: the code, and a security rule. The code, using Firebase’s setDoc and getDoc:

import { getAuth } from 'firebase/auth';
import { getFirestore, doc, getDoc, setDoc } from 'firebase/firestore';

const auth = getAuth();
const db = getFirestore();

// One document per person, named by their user ID
const settingsDoc = () => doc(db, 'settings', auth.currentUser.uid);

async function saveSettings(settings) {
  await setDoc(settingsDoc(), settings);
}

async function loadSettings() {
  const snap = await getDoc(settingsDoc());
  return snap.exists() ? snap.data() : null; // null: nothing saved yet
}

The rule, in firestore.rules, deployed from the console or with firebase deploy. It’s the owner-only rule from Firebase’s docs, for a collection named settings:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /settings/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

With jstash Apps

Set it up once in the dashboard:

  1. Sign in to the jstash dashboard with GitHub and open Apps → New app.
  2. Choose Firebase and paste your Project ID, from Project settings → General in the Firebase console.
  3. Add the website addresses your app runs on, like https://my-app.web.app (up to 5). Turn on Allow localhost while you develop.

Then the code. It’s plain fetch, with nothing to install:

import { getAuth } from 'firebase/auth';

const auth = getAuth();
const JSTASH = 'https://api.jstash.app/v1/apps/app_…/me'; // from the jstash dashboard

// The signed-in person's Firebase ID token
const token = () => auth.currentUser.getIdToken(); // Firebase refreshes it for you

async function save(name, value) {
  const res = await fetch(`${JSTASH}/${name}`, {
    method: 'PUT',
    headers: { Authorization: `Bearer ${await token()}` },
    body: JSON.stringify(value),
  });
  if (!res.ok) throw new Error((await res.json()).error.message);
}

async function load(name) {
  const res = await fetch(`${JSTASH}/${name}`, {
    headers: { Authorization: `Bearer ${await token()}` },
  });
  if (res.ok) return res.json();
  const { error } = await res.json();
  if (error.code === 'DOC_NOT_FOUND') return null; // nothing saved yet
  throw new Error(error.message);
}

await save('settings', { theme: 'dark' });
const settings = await load('settings'); // { theme: 'dark' }

There’s no rule to write. Every request reaches only the documents of the person whose token it is. The dashboard gives you a fuller version with your app’s address filled in, and the Firebase section of the Apps guide has the details.

Side by side

FeatureFirebase (Firestore)jstash Apps
Data modelDocuments in collections, with nested fields and subcollections. Up to 1 MiB eachNamed JSON documents for each person: up to 200, of up to 100 KB each (1 MB on Pro). A save replaces the whole document
QueriesFilters and sorting, plus count, sum and averageNone. Load a document by name, or list one person’s document names
Sharing between usersYes. Your rules decide who reads and writes what, including rolesNo. Each document belongs to one person
Realtime and offlineLive listeners. Offline cache: on by default on Android and Apple, opt-in on the webNo. Load the latest copy when you need it
Security setupSecurity rules you write, test and deployOne rule, built in. You pick the login, paste one value and list your website addresses
Free tierSpark, no card: 1 GiB stored, 50,000 reads, 20,000 writes and 20,000 deletes a day. Sign-in free for most methodsFree, no card: 1 app and 100 users, 10 MB of storage, 500 saves and 5,000 reads per app a day
Past the free tierSpark: the product stops until its quota resets. Blaze: pay as you go, after a no-cost quota. Budget alerts warn you but don’t cap costsHard caps: requests over a limit are refused and nothing is billed. Pro has 3 apps and 1,000 users; it’s invite-only, US$5 a month at launch
FilesCloud Storage, on the Blaze planNo. JSON only
Server codeCloud Functions, on the Blaze planNone
Mobile SDKsApple, Android and web, plus Unity and C++No SDK. Plain web requests with the login token, from a web page or a native app
Lock-in and exportFirestore’s own API and rules. Your server can read all your data; managed export needs BlazeYou can’t read or export your users’ documents. To move, your app copies each person’s data while they’re signed in

jstash numbers are from its limits. Firebase numbers are from the links in the table, checked on 4 October 2026.

Use Firebase when…

  • Your app needs queries. Search, filters, sorting, counts, leaderboards and feeds all need a database that can query, and Firestore can.
  • People share data. A team’s task list, comments, a chat, or anything with owners and editors. Firestore rules can express who sees what.
  • You want live updates or offline use. Listeners push changes to every open device as they happen, and the cache lets the app keep working without a connection.
  • You need more than per-user JSON. Files, server code, push messages, hosting and analytics are all in the same project.
  • You’re building a mobile app. Firebase has native SDKs for Apple and Android.
  • You expect to grow. Sign-in is free for most methods, “even if you have several million users”, and the free Firestore quota is far bigger than jstash’s. jstash Apps tops out at 1,000 users on Pro.
  • You need to see your users’ data. Firestore’s server libraries bypass security rules, so your own tools can read, fix and export everything. jstash gives you no way to read your users’ documents.
  • You already use Firestore. An owner-only rule is a few lines. One service is simpler than two.

Use jstash when…

  • Each person just needs their own data. Settings, progress, drafts, saved items or game state that nobody else needs to see.
  • You don’t want to write rules. There’s no rules language. The one rule is built in, and you can’t turn it off.
  • Your app signs in with Clerk. Firestore’s rules see Firebase Authentication users. Another login means minting custom tokens on your server. Clerk’s Firebase integration is deprecated: “For new applications, you cannot enable the integration.” jstash checks Clerk session tokens itself. See per-user storage for a Clerk app.
  • Your app is a static site with no server. Plain fetch, no SDK to install, and no jstash key in the page.
  • You’d rather hit a limit than a bill. At a limit, the request is refused with an error that names it. Nothing is billed.
  • Your app is small. Up to 100 people on Free, or 1,000 on Pro, which is invite-only.

Using both

jstash Apps accepts Firebase Authentication tokens. So you can keep Firebase for sign-in and keep each person’s JSON in jstash, with no Firestore rules to write. The code is the jstash version above.

  • Setup: create a jstash app, choose Firebase and paste your Project ID. Each jstash app trusts one Firebase project, and that can’t change once people have saved data.
  • Tokens: Firebase ID tokens last an hour. getIdToken() returns the current one, or a fresh one when it’s about to expire, so call it before each request.
  • Wait for sign-in: auth.currentUser can be null until Firebase has finished starting up. Load from onAuthStateChanged.
  • Anonymous sign-ins: Firebase’s guest accounts are refused on Free. Pro accepts them if you turn on Allow anonymous users.
  • The trade-off: each request hands jstash a Firebase ID token, which still works with your Firebase project until it expires, up to an hour. jstash uses it only to check who’s signed in, and doesn’t store or log it. If that’s not acceptable for your app, keep the data in Firestore.
  • Mixing: you could keep shared data in Firestore and per-person JSON in jstash. But if you already write Firestore rules for the shared part, one more owner-only rule is usually simpler than a second service.

What jstash doesn’t do

  • No queries, search, indexes or partial updates. You load or replace a whole document by name.
  • No shared data, teams or roles.
  • No live sync and no offline support.
  • No way for you to read or export your users’ documents. Your app can read a person’s documents only while that person is signed in. You can delete a person’s data when they ask.
  • No files, server functions, push messages or hosting.
  • No SDK or npm package. It’s a small REST API you call with fetch.
  • No backups or old versions. A replaced or deleted document can’t be recovered, so keep your own copy of anything that matters.
  • No uptime guarantee or SLA. jstash is a small service. If it ever shuts down, the Terms promise at least 30 days’ notice.
  • Logins: Firebase Authentication, Clerk, Supabase and Google only. No anonymous users on Free.
  • No self-serve Pro yet: Pro is invite-only, and new invites aren’t being sent yet.
  • Not for sensitive data such as health information.

Which to pick

  • Your app needs anything beyond each person’s own data, or may grow past 1,000 people: Firebase.
  • You already keep data in Firestore: stay there and add an owner-only rule.
  • Each person just needs their own JSON, and you’d rather not write or maintain rules: jstash Apps, with Firebase sign-in or another supported login.

If you start with jstash and outgrow it, moving means your app loads each person’s documents while they’re signed in and saves them to the new database. Plan for that if you expect to grow.

More guides

Using Firebase sign-in? The Firebase section of the Apps guide has the full setup. Free includes 1 app with up to 100 users, no card. How jstash Apps works.