@flarequery/firebase 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # flarequery
2
+
3
+ **Declarative, field-mask-aware data fetching for Firestore.**
4
+ Stop paying for fields you never read.
5
+
6
+ [![npm version](https://img.shields.io/npm/v/@flarequery/firebase.svg)](https://www.npmjs.com/package/@flarequery/firebase)
7
+ [![license](https://img.shields.io/npm/l/@flarequery/firebase.svg)](./LICENSE)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
9
+
10
+ ---
11
+
12
+ ## The Problem
13
+
14
+ Every Firestore fetch returns the **entire document** — whether you asked for 2 fields or 20. You pay for all of them.
15
+
16
+ FlareQuery lets you declare exactly what you need. It builds field masks, resolves relations in parallel, and returns only what was asked for.
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ yarn add @flarequery/firebase
24
+ ```
25
+
26
+ > Node 18+, firebase-admin ≥11, firebase-functions ≥4
27
+
28
+ ---
29
+
30
+ ## Quick Start
31
+
32
+ ```ts
33
+ import { createServerlessApp, one, many } from "@flarequery/firebase";
34
+
35
+ const app = createServerlessApp({ firestore: db, auth });
36
+
37
+ app.model("Product", {
38
+ source: { path: "products" },
39
+ fields: {
40
+ name: "string",
41
+ price: "number",
42
+ category: one("Category", { from: "categoryId", select: ["name"] }),
43
+ tags: many("Tag", { from: "tagIds", select: ["label"] }),
44
+ },
45
+ auth: (ctx) => ctx.userId !== null,
46
+ });
47
+ ```
48
+
49
+ ```ts
50
+ const response = await app
51
+ .collection("Product")
52
+ .doc("prod_abc")
53
+ .select("name", "price", "category.name")
54
+ .get(ctx);
55
+
56
+ // response.data → { name: "...", price: 299, category: { name: "Cameras" } }
57
+ ```
58
+
59
+ Relations resolved in parallel. Only declared fields hit the wire.
60
+
61
+ ---
62
+
63
+ ## Cloud Function
64
+
65
+ ```ts
66
+ export const query = createOnRequest(app, getAuth(), { cors: true });
67
+ ```
68
+
69
+ ```json
70
+ POST /query
71
+ Authorization: Bearer <firebase_id_token>
72
+
73
+ { "model": "Product", "id": "prod_abc", "select": ["name", "price", "category.name"] }
74
+ ```
75
+
76
+ ```json
77
+ {
78
+ "data": { "name": "EOS R5", "price": 3899, "category": { "name": "Cameras" } }
79
+ }
80
+ ```
81
+
82
+ Gen 1: `createFunction` — Gen 2: `createOnRequest`
83
+
84
+ ---
85
+
86
+ ## Auth
87
+
88
+ ```ts
89
+ import { extractContext } from "@flarequery/firebase";
90
+
91
+ const ctx = await extractContext(req.headers.authorization, auth);
92
+ // { userId: string | null, token: DecodedIdToken | null }
93
+ ```
94
+
95
+ Auth rules run before any Firestore read. Unauthorized access throws a `PlanError`.
96
+
97
+ ---
98
+
99
+ ## Field Types
100
+
101
+ | Type | Usage |
102
+ | ------------------- | --------------------------------------- |
103
+ | `"string"` | Scalar string |
104
+ | `"number"` | Scalar number |
105
+ | `"boolean"` | Scalar boolean |
106
+ | `"timestamp"` | Firestore Timestamp |
107
+ | `one(model, opts)` | Single related document via foreign key |
108
+ | `many(model, opts)` | Multiple related documents via ID array |
109
+
110
+ ---
111
+
112
+ ## Error Handling
113
+
114
+ | Class | When |
115
+ | ---------------- | ------------------------------------------------- |
116
+ | `PlanError` | Invalid model, unknown field, unauthorized access |
117
+ | `ExecutionError` | Runtime — missing doc, unresolvable ref |
118
+
119
+ `ExecutionError`s surface in `response.errors[]` — partial data is still returned.
120
+
121
+ ---
122
+
123
+ ## License
124
+
125
+ MIT © Gaurav Paliwal