iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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,functions
Try it Yourself »

Discussion

Loading…