Designing an E-commerce App for Firebase's Free Tier — and Staying There

An image-heavy marketplace can burn through Firebase's free quotas in days. Mizumi Store — a second-hand marketplace for appliances and furniture in Japan — was designed around those budgets from the first commit, and it still ships an admin back office, order tracking, and three languages.
The budget is the architecture
| Resource | Free tier | Our design |
|---|---|---|
| Firestore reads | 50k/day | Paginated listings, cached categories |
| Storage | 1 GB | Images compressed client-side, < 50 KB |
| Hosting bandwidth | 10 GB/mo | Compressed images + aggressive cache headers |
| Functions | 125k invocations/mo | Reserved for the payment webhook |
Write the numbers on the wall. Every feature proposal then has a cost question attached: how many reads does this page really need?
Compress before you upload
The highest-leverage decision was also the simplest: images never leave the client above 50 KB.
const compressed = await imageCompressor(file, {
maxWidth: 1000,
quality: 0.72,
mimeType: 'image/webp',
})
A 3 MB phone photo becomes a 40-ish KB WebP before it touches Storage. That single pipeline keeps storage and bandwidth flat as listings grow — and makes the marketplace feel faster on Japanese mobile networks.
Pagination is not optional
Loading a full collection to filter client-side is how you spend 50k reads before lunch. Listings are cursor-paginated; category pages are cached; the admin back office fetches narrow slices with explicit limits. Firestore rewards you for treating reads as a currency, because they are one.
Admin workflows are first-class features
Most "free-tier" side projects skip the back office and die of manual Firestore edits. Mizumi shipped admin product/category CRUD, order status management, and role checks from the start — compressed images and all.
The payment flow nobody gets for free
The PayPay dynamic QR flow is specified end to end — create an order, generate the QR, confirm on callback, update order state — with Cloud Functions reserved exclusively for the webhook. It is the one piece explicitly marked as future work, and the reservation itself is the point: knowing which single path needs a server is what keeps everything else safely static.
What this costs you
- More code on the client (compression, pagination helpers) than a fat client page would need.
- A cache discipline: when a product changes, something must invalidate.
- Explicit limits in admin tooling — the back office pages too.
The trade is honest: a little more engineering in exchange for a product that runs at zero infrastructure cost and does not surprise you with a bill on the first busy weekend.
