Cryptocurrency Tracker docs
v2.0.0
Live demoConsole demo Get help
● Set up · firebase/

Firebase backend
one API, four jobs.

Create your Firebase project, deploy the Cloud Functions, security rules and indexes, and get the API_BASE_URL the app is built with.

Cloud Functions 2nd genNode 22Blaze plan required

01What runs where

The backend is in firebase/. It is one HTTP API under /v1 plus four scheduled jobs, all on Cloud Functions (2nd gen, Node 22, TypeScript). The app never calls CoinGecko, CoinMarketCap, an RPC node or an AI provider directly: it talks only to this API, Firebase Auth and Firebase Cloud Messaging. That is why no third-party key is ever inside the app.

FunctionTriggerWhat it does
apiHTTPS, 512 MiB, 60 sThe whole /v1 API: markets, global market, coin detail, charts, exchange tickers, search, trending, fiat rates, news, the market brief, coin reports, wallets, the user profile (/v1/me) and account deletion (DELETE /v1/me), alerts, device registration, purchase verification, click tracking and the app configuration (/v1/config).
evaluateAlertsevery 5 minutesChecks every active price alert against current prices and sends the push. See Alerts and push.
refreshNewsevery 15 minutesReads the RSS feeds and keeps headlines and links for 7 days. See News sources.
refreshBriefevery 60 minutesRegenerates the AI market brief per language when it is older than the console's interval (only with AI on and a key). See AI summaries.
sendCampaignsevery 5 minutesSends the push campaigns that are due. It is the only sender of campaigns.

Source: firebase/functions/src/index.ts. The API itself is in firebase/functions/src/api/ and has no Firebase imports, so the same code also runs the public demo on Vercel and the local development server.

02Create the Firebase project

  1. Create a projectOpen console.firebase.google.com, choose Add project and follow the steps. Google Analytics is not needed by the code.
  2. Upgrade to BlazeCloud Functions, Cloud Scheduler (for the four jobs) and outbound network calls to CoinGecko, RPC nodes, RSS feeds and the stores only work on the Blaze (pay-as-you-go) plan. Google bills usage above the free tier to you. Set a budget alert in Google Cloud Billing.
  3. Enable AuthenticationBuild → Authentication → Get started. Enable the Anonymous provider at least: the app signs users in anonymously to call the account endpoints (alerts, purchases, cloud backup).
  4. Create FirestoreBuild → Firestore Database → Create database, in Native mode. Pick a location close to your users; it cannot be changed later.
  5. Cloud MessagingNothing to switch on for Android. For iOS you upload an APNs key, see APNs key.

03Deploy functions, rules and indexes

You need Node.js 22 and the Firebase CLI (Requirements). Sign in once with firebase login.

cd firebase
firebase use --add                 # pick your project; writes .firebaserc (not part of the download)
cd functions && npm ci && cd ..
firebase deploy --only firestore:rules,firestore:indexes,functions

The deploy builds the TypeScript first (predeploy in firebase/firebase.json). The first deploy of scheduled functions may ask you to enable the Cloud Scheduler and Cloud Build APIs; answer yes.

  • Region. The default is us-central1. To change it, copy firebase/functions/.env.example to firebase/functions/.env and set FUNCTIONS_REGION (for example europe-west1) before you deploy. Use the same region in the app's API_BASE_URL.
  • Public invoker. api is deployed with public access. It checks Firebase ID tokens itself for the account endpoints; market data is public by design.
  • Security rules (firebase/firestore.rules) deny everything by default. A signed-in user may read only their own profile and alerts, and read and write only their own optional cloud backup (users/{uid}/sync/watchlist|portfolios|wallets). Configuration, keys, purchases, revenue and statistics are server-only.
Then open the console once

Until the operator console has saved its settings, the backend runs on the built-in defaults: everything that earns or costs money is off, AI is off and CoinGecko is used without a key. Install the console next (Operator console); on start it writes config/public and config/private.

04API_BASE_URL for the app

The app is built with one --dart-define that points at the API. With the default region:

https://us-central1-<your-project-id>.cloudfunctions.net/api

Use your region instead of us-central1. Paths are relative to it: the markets list is <API_BASE_URL>/v1/markets. Check it in a browser:

https://us-central1-<your-project-id>.cloudfunctions.net/api/v1/health      # {"ok":true,"demo":false,...}
https://us-central1-<your-project-id>.cloudfunctions.net/api/v1/markets?perPage=5

Build the app with it, for example flutter build apk --dart-define=API_BASE_URL=https://us-central1-<your-project-id>.cloudfunctions.net/api. See Installation.

05Optional: your own domain with a Hosting rewrite

You can serve the API from your own domain through Firebase Hosting. Add a hosting block to firebase/firebase.json:

"hosting": { "public": "public", "rewrites": [ { "source": "/api/**", "function": { "functionId": "api", "region": "us-central1" } } ] }

Create the public folder (it may hold just an index.html), connect your domain under Hosting in the Firebase console, deploy with firebase deploy --only hosting and build the app with API_BASE_URL=https://<your-domain>/api. The API ignores any prefix before /v1 (normalizePath in firebase/functions/src/api/app.ts). The Hosting CDN then honours the API's Cache-Control: s-maxage headers, which cuts upstream calls further.

Not tested here

The hosting block is not part of the shipped firebase.json; the snippet above comes from firebase/README.md. Test it on your project before you release an app that depends on it.

06Account deletion

Google Play and the App Store require that users can delete an account from inside the app. In the app this is Settings → Delete account. It calls DELETE /v1/me (docs/CONTRACT.md §3.14, firebase/functions/src/api/users.ts).

DeletedKept
users/{uid} with its alerts, devices and sync subcollections, every usage/{uid}_… document, the user's alertEvents, the user's rate-limit window, and then the Firebase Auth account itselfpurchases/* and revenueEvents/*: financial records you may have to keep (tax, refunds, chargebacks). Their uid stays only as an id with no profile behind it. The anonymous daily counters in stats/* also stay.
  • The confirmation dialog tells the user that purchase records are kept and that store subscriptions are not cancelled automatically (they cancel them in their store account). A checkbox also deletes the data on the device.
  • After deletion the app signs out and continues with a new anonymous account.
  • It works for suspended (banned) accounts too, a retry is safe, and it is limited to 5 requests per minute per user. The public demo answers 501 demo_readonly.
  • Mention this retention in your privacy policy.

07Local development

cd firebase/functions
npm ci
npm run dev:api     # the API on http://localhost:8787 (in-memory store, demo config, live keyless CoinGecko)

DEV_AUTH=1 npm run dev:api also accepts Authorization: Bearer dev:<uid> for testing the account endpoints. Without it they answer 501 demo_readonly, like the public demo. PORT=<n> changes the port.

npm run serve       # build + emulators: auth 9099, firestore 8080, functions 5001 (project demo-cryptotracker)

With the emulators the API is at http://127.0.0.1:5001/demo-cryptotracker/us-central1/api/v1/markets. demo-cryptotracker is a Firebase "demo-" project id: the emulators run it without any cloud project. The scheduled jobs are listed but not triggered in the emulator (there is no Pub/Sub emulator); the tests call them directly. The emulators need Java 21 or newer.

08Tests

cd firebase/functions
npm run build && npm run lint
npm test            # unit: every endpoint against fixtures, no network
npm run test:emu    # integration on the Auth + Firestore emulators
npm run test:rules  # security rules on the Firestore emulator
npm run test:live   # optional smoke test against the real free sources (about 12 requests)

When these docs were written, npm run build passed and npm test reported 111 passing unit tests (checked again at the 2.0.0 release commit).

09Firestore collections

For reference. You never edit these by hand: the backend and the console write them (one writer per field, docs/CONTRACT.md §6).

PathContentsWritten by
config/public, config/privateApp settings; server-only keysConsole
users/{uid}Profile, Pro from the store (proStore) or the console (proAdmin), remove-ads, AI credits, banBackend and console (separate fields)
users/{uid}/alerts, users/{uid}/devicesPrice alerts, push tokensBackend (the console may only disable an alert)
users/{uid}/syncOptional cloud backup of watchlist, portfolios and walletsThe user's own app
alertEvents, stats, providerUsage, cache, newsItems, briefs, coinReports, usage, purchases, rateLimitsRuntime dataBackend
revenueEventsRealised revenue onlyBackend (store purchases), console (bookings, payouts, ad reports)
pushCampaignsScheduled push messagesConsole creates and cancels; backend sends