JSPM

  • Created
  • Published
  • Downloads 115
  • Score
    100M100P100Q120820F
  • License MIT

A shared library for standardizing Firestore document schemas and paths across multiple projects

Package Exports

  • @jobsearch-works/firestore-models
  • @jobsearch-works/firestore-models/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@jobsearch-works/firestore-models) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Firestore Models

npm version license

Type-safe Firestore document models and path builders.

A shared library for standardizing Firestore document schemas and paths across multiple projects.

This library provides:

  • Type-safe Firestore document models
  • Pure Firestore path string builders (SDK-free)
  • Centralized schema definitions for use across apps

📦 Installation

npm install @jobsearch-works/firestore-models

Single Source of Truth

This package is the single source of truth for:

  • Database paths (@firestorePaths.ts)
  • Document types (@types)
  • Static data (@static)

Example usage:

// Paths (from @firestorePaths.ts)
import { firestorePaths } from "@jobsearch-works/firestore-models";
const path = firestorePaths.clients.jobPreferences(
  "hovdKgHzIDS7c8Kzt13QNG0xsEN2"
);

// Types (from @types)
import { ClientJobPreferences } from "@jobsearch-works/firestore-models";
const data: ClientJobPreferences = {
  /* ... */
};

// Static data (from @static)
import { locations } from "@jobsearch-works/firestore-models/static/locations";
import jobClassifications from "@jobsearch-works/firestore-models/static/jobClassification";

Quick Start

import { firestorePaths } from "@jobsearch-works/firestore-models";
import { doc, getDoc } from "firebase/firestore";
import { firestore } from "./firebaseConfig";

// Get a client's job preferences
const clientId = "hovdKgHzIDS7c8Kzt13QNG0xsEN2"; // Real Firebase ID
const path = firestorePaths.clients.jobPreferences(clientId);
const ref = doc(firestore, path);
const snap = await getDoc(ref);
const data = { id: snap.id, ...snap.data() };

Type Naming Convention

Types follow their Firestore paths. For example:

  • clients/{id}/emailTokenClientEmailToken
  • clients/{id}/jobPreferencesClientJobPreferences
  • clients/{id}/personalDataClientPersonalData

This makes it easy to find the right type for any path.

Type Casting Results

import { ClientJobPreferences } from "@jobsearch-works/firestore-models";
import { doc, getDoc } from "firebase/firestore";

// 1. Get the document
const path = firestorePaths.clients.jobPreferences(
  "hovdKgHzIDS7c8Kzt13QNG0xsEN2"
);
const ref = doc(firestore, path);
const snap = await getDoc(ref);

// 2. Cast the result (pick one method)
const data = { id: snap.id, ...snap.data() } as ClientJobPreferences;

// OR use the helper function
function withId<T>(snap: DocumentSnapshot): T & { id: string } {
  return { id: snap.id, ...snap.data() } as T & { id: string };
}
const data = withId<ClientJobPreferences>(snap);

React + Zustand Example

import { create } from "zustand";
import { doc, onSnapshot } from "firebase/firestore";
import { firestorePaths } from "@jobsearch-works/firestore-models";
import { ClientJobPreferences } from "@jobsearch-works/firestore-models";

// Store
interface JobPreferencesStore {
  preferences: ClientJobPreferences | null;
  loading: boolean;
  error: Error | null;
  fetchPreferences: (clientId: string) => void;
}

export const useJobPreferences = create<JobPreferencesStore>((set) => ({
  preferences: null,
  loading: false,
  error: null,
  fetchPreferences: (clientId: string) => {
    set({ loading: true });
    const path = firestorePaths.clients.jobPreferences(clientId);
    const ref = doc(firestore, path);

    return onSnapshot(
      ref,
      (snap) => {
        const data = { id: snap.id, ...snap.data() } as ClientJobPreferences;
        set({ preferences: data, loading: false });
      },
      (error) => set({ error, loading: false })
    );
  },
}));

// Component
function JobPreferences() {
  const { preferences, loading, error, fetchPreferences } = useJobPreferences();
  const clientId = "hovdKgHzIDS7c8Kzt13QNG0xsEN2";

  useEffect(() => {
    const unsubscribe = fetchPreferences(clientId);
    return () => unsubscribe();
  }, [clientId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!preferences) return null;

  return (
    <div>
      <h2>Job Preferences</h2>
      <p>Desired Positions: {preferences.desiredPositions.join(", ")}</p>
      <p>
        Min Salary: {preferences.minSalary} {preferences.currency}
      </p>
    </div>
  );
}

Path Structure

// Collection paths
firestorePaths.clients.collection(); // "clients"
firestorePaths.clients.emails.collection("hovdKgHzIDS7c8Kzt13QNG0xsEN2"); // "clients/hovdKgHzIDS7c8Kzt13QNG0xsEN2/emails"

// Document paths
firestorePaths.clients.doc("hovdKgHzIDS7c8Kzt13QNG0xsEN2"); // "clients/hovdKgHzIDS7c8Kzt13QNG0xsEN2"
firestorePaths.clients.personalData("hovdKgHzIDS7c8Kzt13QNG0xsEN2"); // "clients/hovdKgHzIDS7c8Kzt13QNG0xsEN2/personalData"

Type Safety

import {
  ClientJobPreferences,
  JobCategory,
  JobTitle,
  LocationId,
} from "@jobsearch-works/firestore-models";

// Type-safe job preferences
const preferences: ClientJobPreferences = {
  clientId: "hovdKgHzIDS7c8Kzt13QNG0xsEN2",
  desiredPositions: ["Software Engineering"], // TypeScript autocomplete
  categories: ["Information & Communication Technology"], // TypeScript autocomplete
  locations: ["sydney", "london"], // TypeScript autocomplete
  minSalary: 100000,
  currency: "AUD",
  industries: ["Technology"],
  createdAt: new Date(),
  updatedAt: new Date(),
};

Static Data

import jobClassifications from "@jobsearch-works/firestore-models/static/jobClassification";
import { locations } from "@jobsearch-works/firestore-models/static/locations";

// Job categories and titles
type JobCategory = keyof typeof jobClassifications;
type JobTitle = (typeof jobClassifications)[JobCategory][number];

// Location IDs
type LocationId = (typeof locations)[number]["id"];

Installation

npm install @jobsearch-works/firestore-models

Consumption Policy (Important)

  • Always consume this library via the published npm package (semver), e.g. ^4.x.
  • Do not link it via local file paths (e.g. "file:../firestore-models") in downstream projects.
  • Reason: CI/CD and remote environments cannot resolve local file links; using the registry guarantees consistent, reproducible builds.
  • If you need changes, bump the version here and update dependents to that version.

Example in a consumer project's package.json:

{
  "dependencies": {
    "@jobsearch-works/firestore-models": "^4.2.3"
  }
}