Social Metrics API (metrics.chrisvogt.me)
This repository contains a Firebase-backed service I use to fetch and sync data for widgets on my personal website, www.chrisvogt.me.
- Multi-Platform Data Sync: Integrates with Spotify, Steam, Goodreads, Instagram, Discogs, and Flickr
- Firebase Authentication: Secure user authentication with email/password, phone, and Google sign-in
- Session Management: Secure session cookies with JWT token fallback
- Real-time Data: Live data fetching and caching for widget content
- Local Development: Full Firebase emulator support for development
- Node.js (version in .nvmrc, e.g. 24)
- pnpm (e.g.
corepack enable && corepack prepare pnpm@9.15.0 --activate, or see pnpm.io) - Firebase CLI (
pnpm add -g firebase-toolsornpm install -g firebase-tools) - Firebase project and
firebase login
-
Clone and install all dependencies
git clone git@github.com:chrisvogt/metrics.git cd metrics pnpm installThis installs dependencies for the root,
hosting/, andfunctions/in one go (see Monorepo below). -
Configure environment (local development)
Infunctions/, copy the env template and set values (see Environment variables below):cp functions/.env.template functions/.env # Edit functions/.env (CLIENT_API_KEY, CLIENT_AUTH_DOMAIN, CLIENT_PROJECT_ID, etc.)
For local development, copy the template and set your values:
# In the /functions directory
cp .env.template .env
# Edit .env with your actual valuesImportant: Never commit functions/.env to version control. It contains sensitive information like API keys.
The following variables are required for the authentication system to work:
CLIENT_API_KEY- Your Firebase API keyCLIENT_AUTH_DOMAIN- Your Firebase auth domain (e.g.,your-project.firebaseapp.com)CLIENT_PROJECT_ID- Your Firebase project ID
NODE_ENV- Set todevelopmentfor local developmentGEMINI_API_KEY- For AI-powered summaries (if using Gemini integration)
The Firebase client config (API key, auth domain, project ID) is served from the backend so it isn’t hardcoded in the client.
Set CLIENT_API_KEY, CLIENT_AUTH_DOMAIN, and CLIENT_PROJECT_ID in your functions/.env file.
Production config lives in Google Cloud Secret Manager as the secret FUNCTIONS_CONFIG_EXPORT (one JSON object with all keys). To create or update it: run firebase functions:config:export, or in Secret Manager add a new version of that secret with JSON matching the shape in functions/config/exported-config.ts (see CONFIG_PATH_TO_ENV; e.g. auth.client_api_key, github.access_token, spotify.client_id, etc.).
This repo is a pnpm workspace with two packages: hosting (React app) and functions (Firebase Cloud Functions). Turborepo runs tasks across the workspace: it only runs a script in packages that define it, caches outputs, and avoids redundant work.
Use the root for all commands. Do not run npm or pnpm install inside hosting/ or functions/; use pnpm install at the repo root once.
| Command | What it does |
|---|---|
pnpm install |
Install dependencies for root and all workspace packages (single lockfile: pnpm-lock.yaml). |
pnpm run build |
Build the hosting app (Vite → hosting/dist). Only the hosting package has a build script. |
pnpm run dev |
Start the hosting app’s Vite dev server (hot reload). For API calls to work, also run the Functions + Auth emulators (see Development). |
pnpm run dev:full |
Start the Functions + Auth emulators and the Vite dev server in one command (recommended for local dev). |
pnpm run lint |
Lint the functions package (ESLint). |
pnpm run test |
Run functions unit tests (Vitest), single run. |
pnpm run test:coverage |
Run functions tests with coverage. |
pnpm run deploy:all |
Build hosting, then deploy everything (hosting + functions + Firestore rules, etc.) to Firebase. |
pnpm run deploy:hosting |
Build hosting, then deploy only Firebase Hosting. |
pnpm run deploy:functions |
Deploy only Cloud Functions (no build). |
Note: Use pnpm run deploy:all (with run). The bare pnpm deploy is pnpm’s own command and is not our Firebase deploy.
One command (emulators + Vite together):
pnpm run dev:fullOr use two terminals:
# Terminal 1: start Functions + Auth emulators
firebase emulators:start --only functions,auth
# Terminal 2: start the hosting app (from repo root)
pnpm run devOpen http://localhost:5173. The Vite dev server proxies /api to the Functions emulator. Sign-in and API testing work against the emulators. If you run only pnpm run dev without the emulators, API requests will receive a 503 with a message to start the backend.
Build the hosting app once, then run all emulators so the site is served like production:
# From repo root: build the React app, then start emulators
pnpm run build
firebase emulators:start --only hosting,functions,authOpen the Hosting URL (e.g. http://metrics.dev-chrisvogt.me:8084). Same rewrites as production: /api/** → Cloud Function, ** → SPA.
| Service | URL |
|---|---|
| Emulator UI | http://127.0.0.1:4000 |
| Hosting | http://127.0.0.1:8084 (or configured host) |
| Functions | http://127.0.0.1:5001 |
| Auth | http://127.0.0.1:9099 |
| Firestore | http://127.0.0.1:8080 |
The application includes a comprehensive authentication system with:
- Email/Password Login: Traditional email and password authentication
- Phone Authentication: SMS-based verification with Firebase Phone Auth
- Google Sign-In: OAuth authentication with Google accounts
- Session Management: Secure HTTP-only cookies with JWT fallback
- Multi-tenant Support: Ready for future multi-user expansion
The following endpoints are available:
| Widget | Description | Auth Required |
|---|---|---|
/api/widgets/spotify |
GET Spotify widget content | No |
/api/widgets/sync/spotify |
Trigger Spotify data sync | Yes |
/api/widgets/steam |
GET Steam widget content | No |
/api/widgets/sync/steam |
Trigger Steam data sync | Yes |
/api/widgets/goodreads |
GET Goodreads widget content | No |
/api/widgets/sync/goodreads |
Trigger Goodreads data sync | Yes |
/api/widgets/instagram |
GET Instagram widget content | No |
/api/widgets/sync/instagram |
Trigger Instagram data sync | Yes |
/api/widgets/discogs |
GET Discogs widget content | No |
/api/widgets/sync/discogs |
Trigger Discogs data sync | Yes |
/api/widgets/flickr |
GET Flickr widget content | No |
/api/widgets/sync/flickr |
Trigger Flickr data sync | Yes |
/api/auth/session- Create session cookies (POST)/api/firebase-config- Get Firebase client configuration (GET)
- React + Vite app in
hosting/: sign-in (Google, email, phone) and API testing dashboard - Firebase SDK: Client-side auth; config loaded at runtime from
/api/firebase-config - Session: Cookie-based sessions (created via
/api/auth/session) with JWT fallback - Build: From repo root,
pnpm run build→hosting/dist; Firebase Hosting serves that folder and rewrites/api/**to the Cloud Function and**to/index.html(SPA)
See hosting/README.md for hosting-only scripts and local dev details.
- Firebase Functions: Serverless backend with Express.js (written in TypeScript)
- Firebase Auth: User authentication and session management
- Firestore: Data storage and caching
- External APIs: Integration with various platform APIs
The functions/ package is TypeScript. Source lives in functions/*.ts and role-based dirs: config/, widgets/, utils/, helpers/ (plus api/, jobs/, transformers/, etc.). pnpm run build (from repo root or from functions/) compiles with tsc and outputs only to functions/lib/; lib/ is gitignored. Firebase deploys from lib/. Run pnpm run build before pnpm run deploy:functions if you’ve changed function code. Tests (pnpm run test) and lint (pnpm run lint) run against the TypeScript source.
- CORS Protection: Configurable cross-origin resource sharing
- Rate Limiting: Built-in request throttling
- Session Validation: Secure session cookie validation
- Environment Isolation: Separate configs for development/production
Tests live in functions/. From repo root:
pnpm run test # run once
pnpm run test:coverage # with coverageFor watch mode, run from the functions package: pnpm --filter metrics-functions run test:watch.
The hosting app must be built before deploy (the root deploy:all script does this automatically). All commands from repo root; see Monorepo for the full command list.
pnpm run build # build hosting app only (output: hosting/dist)
pnpm run deploy:all # build + deploy everything (hosting + functions + Firestore, etc.)
pnpm run deploy:hosting # build + deploy hosting only
pnpm run deploy:functions # deploy Cloud Functions only (no build)- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Install and set up (see How to install and Environment variables)
- Run tests:
pnpm run test(from repo root) - Ensure the hosting app builds:
pnpm run build(from repo root) - Commit your changes and open a Pull Request
Copyright © 2020-2025 Chris Vogt. Released under the MIT License.