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
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-modelsSingle 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}/emailToken→ClientEmailTokenclients/{id}/jobPreferences→ClientJobPreferencesclients/{id}/personalData→ClientPersonalData
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-modelsConsumption 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"
}
}