Skip to content

Firebase Provider

Setup

Auth uses a service account JSON file from the Firebase console.

  1. Go to Firebase console → Project settings → Service accounts
  2. Click Generate new private key → download the JSON file
  3. Store it somewhere safe (outside version control)
FIREBASE_SA=./firebase/service-account.json

The firebase option in @Deploy is optional - if FIREBASE_SA is set in your environment, it's picked up automatically.

// Explicit
@Deploy({ firebase: process.env.FIREBASE_SA! })

// Or let it read FIREBASE_SA from env
@Deploy({ dryRun: true })

Local Emulator Integration

Puls supports local development using the Firebase Local Emulator Suite (specifically Firebase Auth and Cloud Firestore).

When you configure the standard Firebase emulator environment variables, Puls automatically intercepts API requests, bypasses production service account checks, and routes operations to your local emulators:

# Set emulator hosts in your shell before running Puls
export FIRESTORE_EMULATOR_HOST=localhost:8080
export FIREBASE_AUTH_EMULATOR_HOST=localhost:9099

When these environment variables are active: 1. Token Bypass: Puls does not require a valid FIREBASE_SA service account file and uses mock access credentials. 2. Firestore Rules: Security rules are deployed directly to the local Firestore emulator rules engine. 3. Firestore Indexes & Auth Config: Since the local emulators do not support management REST APIs for indexes and authentication provider setups, Puls automatically intercepts these calls and simulates successful results locally so that stacks deploy and tear down cleanly without errors.


Hosting

Deploy a static site from a local build directory.

Firebase.Hosting("your-site-id")
  .source("./dist")   // path to your built static files
  .domain("example.com") // optional custom domain

The site ID is the Firebase Hosting site name - by default it matches your project ID (e.g. my-projecthttps://my-project.web.app). You can override the displayed URL using .domain("yourdomain.com").

Deploy flow:

  1. Creates a new Hosting version
  2. SHA256-hashes all files in the source directory
  3. Sends the hash map to Firebase - only files that changed are uploaded
  4. Finalizes the version and creates a release
  5. Outputs the live URL

Idempotency: Each deploy creates a new release (Firebase's native model). Previous releases remain in the console but are inactive.


App Check

Declaratively manage App Check attestation enforcement modes for primary backend services.

Firebase.AppCheck()
  .enforce("firestore")
  .unenforced("storage")
  .off("auth");

Supported Services:

Service Name API Target
firestore firestore.googleapis.com (Cloud Firestore)
storage firebasestorage.googleapis.com (Cloud Storage)
database firebasedatabase.googleapis.com (Realtime Database)
auth identitytoolkit.googleapis.com (Firebase Authentication)

On deploy, Puls queries the current enforcement statuses and patches services whose configurations differ. In teardowns (destroy()), Puls automatically reverts all configured services back to "OFF" to leave the environment clean.


Firestore

Deploy Cloud Firestore security rules and configure composite indexes.

Firebase.Firestore()
  .rules("./firestore.rules")
  .indexes("./firestore.indexes.json");

Storage

Manage Cloud Storage rules and CORS policies.

Firebase.Storage("my-bucket")
  .rules("./storage.rules")
  .cors([
    {
      origin: ["*"],
      method: ["GET", "POST", "PUT"],
      responseHeader: ["Content-Type"],
      maxAgeSeconds: 3600,
    },
  ]);

Auth

Configure Authentication sign-in providers and authorized domains.

Firebase.Auth()
  .email(true)                    // Enable email/password sign-in
  .anonymous(true)                // Enable anonymous access
  .authorizedDomains(["example.com", "my-app.web.app"]);

Remote Config

Declare typed parameters and conditions for Firebase Remote Config templates with ETag-safe PUT operations.

Firebase.RemoteConfig()
  .string("welcome_message", "Hello, World!")
  .bool("feature_flag_active", true)
  .number("max_retries", 3)
  .condition("is_android", "device.os == 'android'")
  .override("welcome_message", "is_android", "Hello from Android!");

Full example

import "dotenv/config";
import "reflect-metadata";
import { Stack, Deploy } from "@puls-dev/core";
import { Firebase } from "@puls-dev/firebase";

@Deploy({ dryRun: false })
class AppStack extends Stack {
  // Static Hosting
  site = Firebase.Hosting("my-app-hosting")
    .source("./dist")
    .domain("myapp.io");

  // Backend Security Attestation
  appcheck = Firebase.AppCheck()
    .enforce("firestore")
    .enforce("storage");

  // Database Rules
  db = Firebase.Firestore()
    .rules("./firestore.rules");

  // Storage configuration
  bucket = Firebase.Storage("my-app-media")
    .rules("./storage.rules");

  // Authentication configuration
  auth = Firebase.Auth()
    .email(true)
    .authorizedDomains(["myapp.io"]);
}

Build your site first, then deploy:

# Web application build example
npm run build              # outputs static assets to ./dist
npx tsx examples/deploy.ts

Service account permissions

The service account needs the Firebase Hosting Admin, App Check Admin, Cloud Datastore Owner, and App Engine Admin roles depending on which features you use.

Ensure these roles are granted via IAM to the service account email in your GCP Console under IAM & Admin → IAM.