Local Emulators
Firebase Local Emulator Suite runs Auth, Firestore, Realtime DB, Functions, Storage, Hosting, Pub/Sub, and Extensions on your laptop. You get instant iteration, no quota burn, deterministic tests, and offline development.
Setup, seeding, CI, security rules
EXAMPLE
// 1) Install + init
npm install -g firebase-tools
firebase login
firebase init emulators
// Select: auth, firestore, functions, storage, hosting (whatever you use)
// firebase.json — typical config
{
"emulators": {
"auth": { "port": 9099 },
"firestore": { "port": 8080 },
"storage": { "port": 9199 },
"functions": { "port": 5001 },
"hosting": { "port": 5000 },
"pubsub": { "port": 8085 },
"ui": { "enabled": true, "port": 4000 },
"singleProjectMode": true
},
"firestore": { "rules": "firestore.rules", "indexes": "firestore.indexes.json" },
"storage": { "rules": "storage.rules" }
}
// 2) Start the suite
firebase emulators:start
// Emulator UI: http://localhost:4000
// • Browse Firestore, Auth users, Storage blobs
// • Replay logs, watch function executions
// • Run rules playground against your firestore.rules
// With persistence between runs
firebase emulators:start --import=./seed --export-on-exit=./seed
// Loads ./seed on start, exports current state on Ctrl+C.
// Commit ./seed so the team starts from the same fixture.
// 3) Point the client SDKs at the emulators
import { initializeApp } from 'firebase/app';
import { connectAuthEmulator, getAuth } from 'firebase/auth';
import { connectFirestoreEmulator, getFirestore } from 'firebase/firestore';
import { connectStorageEmulator, getStorage } from 'firebase/storage';
import { connectFunctionsEmulator, getFunctions } from 'firebase/functions';
const app = initializeApp({ projectId: 'demo-app' }); // 'demo-*' = emulator-only, no creds
const auth = getAuth(app);
const db = getFirestore(app);
const storage = getStorage(app);
const functions = getFunctions(app);
if (import.meta.env.DEV || process.env.NODE_ENV === 'test') {
connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true });
connectFirestoreEmulator(db, '127.0.0.1', 8080);
connectStorageEmulator(storage, '127.0.0.1', 9199);
connectFunctionsEmulator(functions, '127.0.0.1', 5001);
}
// 4) Admin SDK in tests / functions
import admin from 'firebase-admin';
process.env.FIRESTORE_EMULATOR_HOST = 'localhost:8080';
process.env.FIREBASE_AUTH_EMULATOR_HOST = 'localhost:9099';
process.env.STORAGE_EMULATOR_HOST = 'http://localhost:9199';
admin.initializeApp({ projectId: 'demo-app' });
// 5) Seeding
async function seed() {
const db = admin.firestore();
const auth = admin.auth();
const alice = await auth.createUser({ uid: 'u_alice', email: 'alice@example.com' });
await db.collection('users').doc(alice.uid).set({ name: 'Alice', plan: 'pro' });
await db.collection('posts').add({
authorId: alice.uid,
title: 'Hello emulators',
createdAt: admin.firestore.FieldValue.serverTimestamp(),
});
}
seed().then(() => process.exit(0));
// Run while the emulator is up:
// FIRESTORE_EMULATOR_HOST=localhost:8080 node seed.mjs
// 6) Testing security rules — @firebase/rules-unit-testing
import { initializeTestEnvironment, assertSucceeds, assertFails }
from '@firebase/rules-unit-testing';
import fs from 'node:fs';
const testEnv = await initializeTestEnvironment({
projectId: 'demo-app',
firestore: { rules: fs.readFileSync('firestore.rules', 'utf8') },
});
test('users can only read their own profile', async () => {
const alice = testEnv.authenticatedContext('alice').firestore();
const bob = testEnv.authenticatedContext('bob').firestore();
await testEnv.withSecurityRulesDisabled(async (ctx) => {
await ctx.firestore().doc('users/alice').set({ name: 'Alice' });
});
await assertSucceeds(alice.doc('users/alice').get());
await assertFails (bob.doc('users/alice').get());
});
afterAll(() => testEnv.cleanup());
// 7) Cloud Functions — local iteration
firebase emulators:start --only functions
// or with hot reload:
cd functions && npm run build:watch // tsc --watch
firebase emulators:start --only functions
// Trigger an HTTP function locally
curl http://localhost:5001/demo-app/us-central1/helloWorld
// 8) Background-trigger functions
await db.collection('users').add({ name: 'Test' });
// onCreate('users/{id}') fires in the emulated Functions runtime — logs in UI.
// 9) Auth emulator quirks
// • All accounts created here are local — they vanish on stop (unless --import/export)
// • Email links and phone codes are PRINTED TO CONSOLE — no real email sent
// • OAuth provider sign-in works in fake mode — choose 'continue as X' in the UI
// • You can call admin.auth().createCustomToken for full control in tests
// 10) CI integration
// GitHub Actions example
// .github/workflows/test.yml
name: test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npx firebase emulators:exec --only auth,firestore --project demo-test "npm test"
// emulators:exec starts the suite, runs your command, tears down.
// 11) Performance + safety
// • The emulator suite uses your machine's resources — give it >= 2 GB free
// • Use 'demo-*' projectId to ensure SDKs refuse to contact prod accidentally
// • Keep production credentials out of dev env files
// • DO NOT mix --import paths between branches — keep one canonical seed per env
// 12) Common bugs
// • SDK still talks to prod — check connectXEmulator was called BEFORE any read/write
// • Auth tokens minted in prod won't work against the emulator (different signing key)
// • Storage emulator paths use http://localhost:9199 — getDownloadURL returns the emulator URL
// • Functions need build step — change to TypeScript not visible until compiled
// • Test env caches user creds — use testEnv.clearFirestore() between tests for isolation
// • CORS errors in browser SDK — check the auth emulator port matches connectAuthEmulator
Why it matters
Run all your local dev and tests against the Emulator Suite, never against production. Pair --import/--export-on-exit with a committed seed so the team and CI start from the same fixture, and gate connectXEmulator calls on a dev/test environment check so a stray build can’t accidentally hit your real project.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
firebase init emulators # Run locally without touching prod firebase emulators:start --only auth,firestore,functionsTry it Yourself »
Discussion
Loading…