Chapter 01 · Firestore Foundations, Firebase/Google Cloud, Editions, Modes, Locations, and Lab Setup

Build a Reproducible Lab with Authentication, Rules, Test Data, Index Configuration, Logging, and Safe Cleanup

Turn Chapter 01 into a reusable Firestore lab contract with Authentication, restrictive Rules, deterministic data, index configuration, client/server verification, logging, evidence capture, and safe reset automation.

Beginner → Advanced120–140 minutesCheckpoint labReusable Chapter 01 harnessLast reviewed: September 2026

Learning outcomes

A useful course lab is not a one-time demo. AtlasMart needs a harness that every later chapter can start, verify, break safely, and reset without touching production or carrying hidden state from the previous exercise.

01

Organize the Chapter 01 files into a repeatable emulator-backed lab with stable project, ports, schema and seed data.

02

Verify client authentication, Security Rules allow/deny cases and privileged server behavior with explicit assertions.

03

Record index definitions, logs and environment identity so later query failures are diagnosable.

04

Define safe reset and optional emulator import/export workflows that cannot delete unrelated cloud data.

05

Produce a verification manifest that states what the local lab proves and which production properties remain unverified.

Chapter baseline reviewed 13 September 2026

The reproducible lab pins Firebase CLI 15.30.0, Firebase JavaScript SDK 12.19.0, Firebase Admin Node.js SDK 14.4.0, and Node.js 22 or newer for the Admin SDK. These are a dated lab baseline, not permanent course guarantees. Firestore service capabilities, editions, pricing, locations, emulator fidelity and SDK support continue to evolve; re-check the official documentation before using the commands later.

Execution and safety note

The generation environment used to build this chapter does not run Firebase Emulator Suite or a billed Google Cloud project. Commands and API shapes were checked against current official documentation, but expected output is described by invariant and evidence shape rather than fabricated as captured output. The mandatory lab uses a Firebase demo project ID and local emulators. Optional production verification must use an isolated test project with explicit billing awareness.

1. The reusable lab contract

The lab should answer five questions on every run: Which project/database surface am I using? Which tool versions are running? Which seed documents exist? Which requests are allowed/denied? What state will cleanup remove? If any answer is implicit, later chapters inherit a hidden dependency.

AtlasMart standardizes on project ID demo-atlasmart-firestore, default emulator database, Firestore port 8080, Auth port 9099 and UI port 4000. Product IDs and schema fields are stable so Chapter 02 can extend the model instead of inventing a new dataset.

Contract item Chapter 01 value Reason
Project ID demo-atlasmart-firestore No live cloud backing; safer mandatory lab
Database (default) emulator database Simple shared Core baseline
Products products/p-1001, products/p-1002 Stable catalog fixtures
Profiles profiles/{uid} Authentication/Rules boundary
Auth Anonymous user in Auth emulator Deterministic untrusted client identity
Server path Admin SDK to Firestore emulator Trusted path without production key

2. Repository-local lab layout

recommended Chapter 01 lab layout
atlasmart-firestore-lab/├─ package.json├─ firebase.json├─ firestore.rules├─ firestore.indexes.json├─ scripts/│  ├─ seed.mjs│  ├─ client-check.mjs│  ├─ server-check.mjs│  └─ verify.mjs└─ .gitignore

Keep configuration files in source control. Keep emulator exports, logs and any real credential files out of source control. The course itself never requires a downloaded service-account JSON key.

.gitignore · protect generated state and credentials
.emulator-data/*.log.env.env.*service-account*.jsonapplication_default_credentials.json

3. Assemble the deterministic configuration

package.json
{  "name": "atlasmart-firestore-lab",  "private": true,  "type": "module",  "engines": { "node": ">=22" },  "dependencies": {    "firebase": "12.19.0",    "firebase-admin": "14.4.0"  },  "devDependencies": {    "firebase-tools": "15.30.0"  },  "scripts": {    "emulators": "firebase emulators:start --project demo-atlasmart-firestore --only auth,firestore",    "seed": "node scripts/seed.mjs",    "client-check": "node scripts/client-check.mjs",    "server-check": "node scripts/server-check.mjs"  }}
firebase.json
{  "firestore": {    "rules": "firestore.rules",    "indexes": "firestore.indexes.json"  },  "emulators": {    "auth": { "port": 9099 },    "firestore": { "port": 8080 },    "ui": { "enabled": true, "port": 4000 },    "singleProjectMode": true  }}
firestore.rules
rules_version = '2';service cloud.firestore {  match /databases/{database}/documents {    match /products/{productId} {      allow read: if true;      allow write: if false;    }    match /profiles/{uid} {      allow read, create, update:        if request.auth != null && request.auth.uid == uid;      allow delete: if false;    }    match /{document=**} {      allow read, write: if false;    }  }}
firestore.indexes.json
{  "indexes": [],  "fieldOverrides": []}

Even an empty index configuration is meaningful: it records that Chapter 01 does not depend on a composite index. Chapter 06 will add index definitions deliberately and measure the consequences.

4. Seed state and verify server-side invariants

scripts/seed.mjs
process.env.FIRESTORE_EMULATOR_HOST = "127.0.0.1:8080";process.env.GCLOUD_PROJECT = "demo-atlasmart-firestore";import { initializeApp } from "firebase-admin/app";import { getFirestore } from "firebase-admin/firestore";initializeApp({ projectId: "demo-atlasmart-firestore" });const db = getFirestore();await db.doc("products/p-1001").set({  name: "Trail Camera",  category: "cameras",  price: 129.90,  public: true,  schemaVersion: 1});await db.doc("products/p-1002").set({  name: "USB-C Hub",  category: "accessories",  price: 49.50,  public: true,  schemaVersion: 1});console.log("seeded AtlasMart emulator documents");process.exit(0);

Run the seed after startup. The expected invariant is exactly two base catalog products plus any temporary documents created by the verification scripts. A later lesson that changes product shape should either migrate these fixtures or update the contract explicitly.

5. Verify client security as executable behavior

scripts/client-check.mjs
process.env.FIREBASE_AUTH_EMULATOR_HOST = "127.0.0.1:9099";process.env.FIRESTORE_EMULATOR_HOST = "127.0.0.1:8080";import { initializeApp } from "firebase/app";import {  getFirestore, connectFirestoreEmulator, doc, getDoc, setDoc} from "firebase/firestore";import {  getAuth, connectAuthEmulator, signInAnonymously} from "firebase/auth";const app = initializeApp({  apiKey: "demo-key",  projectId: "demo-atlasmart-firestore",  appId: "1:123:web:demo"});const db = getFirestore(app);const auth = getAuth(app);connectFirestoreEmulator(db, "127.0.0.1", 8080);connectAuthEmulator(auth, "http://127.0.0.1:9099", { disableWarnings: true });const credential = await signInAnonymously(auth);const uid = credential.user.uid;const product = await getDoc(doc(db, "products", "p-1001"));console.log("public product readable:", product.exists(), product.data()?.name);await setDoc(doc(db, "profiles", uid), {  displayName: "AtlasMart learner",  schemaVersion: 1});console.log("own profile write allowed:", uid);try {  await setDoc(doc(db, "products", "p-client-write"), { name: "forbidden" });  throw new Error("unexpected client product write success");} catch (error) {  console.log("client product write denied as intended:", error.code ?? error.message);}

Security verification must contain both positive and negative cases. “The app worked” is not evidence that access control worked. The forbidden write is as important as the successful read.

6. Verify trusted server behavior without pretending Rules apply

scripts/server-check.mjs
process.env.FIRESTORE_EMULATOR_HOST = "127.0.0.1:8080";process.env.GCLOUD_PROJECT = "demo-atlasmart-firestore";import { initializeApp } from "firebase-admin/app";import { getFirestore } from "firebase-admin/firestore";initializeApp({ projectId: "demo-atlasmart-firestore" });const db = getFirestore();await db.doc("products/p-server-write").set({  name: "Trusted backend fixture",  category: "lab",  price: 1,  schemaVersion: 1});const snap = await db.doc("products/p-server-write").get();console.log("trusted server write/read:", snap.exists, snap.data()?.name);

The trusted server test establishes the separation that many Firebase applications miss: client access is Rules-controlled, server access is IAM/application-controlled. Later IAM chapters will replace “trusted” as a vague label with least-privilege roles and workload identity.

7. Add a single verification runner

A small orchestration script can make the acceptance criteria binary. It should fail the run on unexpected success/failure rather than require a learner to inspect console output manually.

scripts/verify.mjs · fail fast on broken invariants
import { spawn } from "node:child_process";const commands = [  ["node", ["scripts/seed.mjs"]],  ["node", ["scripts/client-check.mjs"]],  ["node", ["scripts/server-check.mjs"]]];for (const [cmd, args] of commands) {  const code = await new Promise((resolve) => {    const child = spawn(cmd, args, { stdio: "inherit", env: process.env });    child.on("exit", resolve);  });  if (code !== 0) process.exit(code ?? 1);}console.log("Chapter 01 verification complete");

Run this from a terminal where the emulator endpoint environment is set for server scripts, or keep those variables inside the scripts as Chapter 01 does. Later CI lessons centralize environment injection.

8. Logging and evidence capture: record facts, not screenshots

Useful evidence includes version output, startup logs, project ID, emulator ports, successful/denied operation assertions, and the final document list. Screenshots can supplement the record but should not be the only proof.

verification checklist · observable evidence
node --versionnpx firebase --versionnpm ls firebase firebase-admin firebase-toolsnpm run seednpm run client-checknpm run server-check# Optional: use Emulator UI only to inspect the same state visually.# Firestore: http://127.0.0.1:4000/firestore# Authentication: http://127.0.0.1:4000/auth

Do not paste real tokens, credential paths or service-account contents into course logs. If a later cloud exercise uses ADC, record the principal identity—not the secret material.

9. Failure injection: make the wrong state safe and reversible

Chapter 01 has three useful failures that require no destructive cloud action:

  • Comment out connectFirestoreEmulator while using a demo project ID. The operation should fail rather than mutate a real cloud project.
  • Change the client write target from profiles/{uid} to profiles/someone-else. Rules should deny it.
  • Stop the Firestore emulator while the client runs. The application should surface an availability/network failure instead of silently claiming success.

After each failure, restore the known configuration and rerun the verification runner. Failure injection is complete only when recovery is verified.

10. Safe reset and optional state export

reset contract · bounded local state
# Default reset: stop with Ctrl+C and restart.# No production data is involved.# Persistent local state for a later exercise:mkdir -p .emulator-datanpx firebase emulators:start \  --project demo-atlasmart-firestore \  --only auth,firestore \  --import=.emulator-data \  --export-on-exit=.emulator-data# To reset exported state:# 1. stop emulators# 2. remove only the repository-local .emulator-data directory# 3. restart and run the seed again

Never turn cleanup into a generic firebase firestore:delete or cloud-recursive-delete command in Chapter 01. The course will teach destructive operations later with explicit blast-radius controls.

11. Acceptance checklist and production non-guarantees

  • Node.js reports 22 or newer; CLI/SDK package versions match the pinned baseline.
  • Firestore/Auth emulator startup identifies the demo project and expected ports.
  • Seed creates deterministic product fixtures.
  • Client public product read succeeds.
  • Authenticated client can write only its own profile.
  • Client product write fails as expected.
  • Admin/server write succeeds, demonstrating the separate trusted path.
  • Restart/reset returns the lab to the same known state.

Passing all checks still does not prove production IAM, App Check, region placement, SLA, real billing, production composite-index behavior, Enterprise query execution, network latency, contention, backup/recovery or production-scale performance. Those are future chapter acceptance criteria.

12. Production judgment and bridge to Chapter 02

The best outcome from Chapter 01 is not “we know Firebase.” It is a reproducible boundary around what has been proven. AtlasMart now has a safe client/server trust model, deterministic fixtures, a resettable emulator environment and a version record. This is enough foundation to discuss document paths, field types, maps, arrays, references, timestamps, GeoPoints and platform limits without environment ambiguity.

Chapter 02 will keep these project and collection names stable while expanding the document model and inspecting serialization/limit behavior across client and server SDKs.

Knowledge check

Check your understanding

  1. Why should an empty `firestore.indexes.json` still be committed in Chapter 01?
  2. What two kinds of Security Rules tests are necessary for meaningful authorization evidence?
  3. Why is the Admin SDK allowed to bypass the Rules file in this lab?
  4. What is the safest default reset for the Chapter 01 emulator lab?
  5. List three important production properties this lab intentionally does not prove.
Review the answers

1. It records the intended index baseline and makes future index changes reviewable. Later chapters can show exactly when and why composite/vector/index exemptions are introduced.

2. Positive allow cases and negative deny cases. Testing only successful operations cannot reveal overbroad authorization.

3. Admin/server libraries are privileged and use IAM/Google credentials in production; Firestore Security Rules apply to mobile/web client paths, not server libraries. The lab demonstrates that architectural boundary.

4. Stop the local emulators and restart them, then rerun deterministic seed scripts. If exported emulator state is used, delete only the repository-local export directory.

5. Examples include production location, billing/SKU behavior, cloud IAM, App Check enforcement, production index enforcement, Enterprise/Pipeline behavior, real latency/SLA, backup recovery and production contention.

Summary and next step

Chapter 01 keeps Firestore claims tied to an observable state surface: client versus server, emulator versus production, project versus database, and Standard versus Enterprise/mode. Preserve the lab evidence and do not convert unknown production facts into assumptions.

Next, continue to Document/Collection Hierarchy, Paths, IDs, Subcollections, and the Absence of Traditional Server-Side Joins in Core Operations.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.