Back to articles
backend July 15, 2026 4 min read

Building ServiceHub’s Geolocation Backend with Firebase Functions and Render

I built ServiceHub’s geolocation engine with Firebase Functions, an Express API on Render, and a lightweight vanilla‑JS admin console—here’s how it all fits together.

How I Built ServiceHub’s Geolocation Backend

When I set out to create ServiceHub I wanted a single source of truth for location data, real‑time matching, and a lightweight admin console. The result is a mix of Firebase Cloud Functions, an Express server hosted on Render.com, and a vanilla‑JS front‑end that talks to both. Below I walk through the key pieces that make it tick.

Project Structure

The repository is split into three logical folders:

Each part has its own package.json so I can version and deploy them independently.

Firebase Functions – The Core Geolocation Engine

The functions/package.json declares the runtime and all the libraries I need for location math:

{
  "name": "servicehub-geo-functions",
  "main": "index.js",
  "scripts": {
    "serve": "firebase emulators:start --only functions",
    "deploy": "firebase deploy --only functions"
  },
  "dependencies": {
    "firebase-admin": "^12.6.0",
    "firebase-functions": "^5.0.1",
    "ngeohash": "^0.6.3",
    "geofire-common": "^6.0.0",
    "cors": "^2.8.5",
    "nodemailer": "^6.9.14",
    "@anthropic-ai/sdk": "^0.39.0"
  }
}

The most useful libraries are ngeohash for encoding lat/long into geohashes and geofire-common for radius queries. With these I can store a user’s location in Firestore and efficiently query “who’s within 5 km?”.

Lazy‑Loading Optional Modules

In app.js I guard non‑essential code (like the notification bell) behind a tiny helper that swallows import errors. This ensures a broken optional module never brings down the whole app:

const safeImport = async (path) => {
  try { return await import(path); }
  catch (e) { console.warn("[boot] optional module failed:", path, e); return null; }
};
await safeImport("./js/notifications/notification-bell.js");

The pattern is simple but powerful: the UI stays responsive even if a third‑party script fails to load in a user’s browser.

Express Render Server – Public API & Rate Limiting

The render-server/package.json spins up a tiny Express app:

{
  "name": "servicehub-render-server",
  "main": "server.js",
  "scripts": { "start": "node server.js", "dev": "node --watch server.js" },
  "dependencies": {
    "express": "^4.19.2",
    "cors": "^2.8.5",
    "express-rate-limit": "^7.4.0",
    "firebase-admin": "^12.6.0",
    "geofire-common": "^6.0.0",
    "ngeohash": "^0.6.3",
    "nodemailer": "^6.9.14"
  }
}

Key choices:

The server file (server.js) wires these together, exposing routes like /api/nearby that call the same geohash utilities used in the Firebase Functions.

Admin UI – Plain HTML + Modular JS

Instead of a heavy framework I opted for a minimal HTML shell that loads a single ES‑module (app.js). The two admin pages (admin-broadcasts.html and admin-notifications.html) are almost identical; the only difference is the hash they force‑navigate to:

<script>
  if (location.hash !== "#admin/broadcasts") location.replace("./#admin/broadcasts");
</script>

The UI pulls in a shared stylesheet, Font Awesome icons, and a small support‑chat widget. All business logic lives in js/ – for example js/router.js creates a client‑side router, and js/utils.js holds helpers like toast and updateWalletHeader.

Development & Deployment Workflow

I keep the three pieces independent but linked by npm scripts:

When I’m ready for production I run:

npm run build   # (if a build step existed)
cd functions && npm run deploy
cd render-server && npm start   # Render.com runs this automatically

The netlify.toml file tells Netlify to publish the static admin UI from the repository root, so the whole stack can be served from a single domain if desired.

Lessons Learned

ServiceHub now supports real‑time proximity alerts, email broadcasts, and a clean admin experience—all built from a handful of well‑chosen npm packages.

Need something like this built?

I work on full-stack web apps — backend systems, APIs, and the front-ends that sit on top. If this post was useful and you've got a project that needs it, I'd like to hear about it.

Want future posts like this?

No mailing list yet — for now, email me and I'll let you know when something new goes up.

Email me